/*
 *
 *
Copyright (c) 1998-2001 Aristar, Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. The end-user documentation included with the redistribution, if any,
must include the following acknowledgment:

"This product includes software developed by Aristar, Inc. (http://www.aristar.com/)."

Alternately, this acknowledgment may appear in the software itself, if and
wherever such third-party acknowledgments normally appear.

4. The names "Dialect" and "Aristar, Inc." must not be used to endorse or
promote products derived from this software without prior written permission.
For written permission, please contact info@aristar.com.

5. Products derived from this software may not be called "Dialect", nor may
"Dialect" appear in their name, without prior written permission of Aristar, Inc.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
ARISTAR, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software consists of voluntary contributions made by many individuals
on behalf of Aristar, Inc.  For more information on Aristar, Inc.,
please see <http://www.aristar.com/>.

Portions of this software are based upon software freely available and
written by individuals not associated with Aristar, Inc.  Copyrights are
maintained in the relevant source code files.
 *
 *
 */

#include "stdafx.h"
#include "DConfigGlobals.h"
#include "DialectInterp.h"
#include "CImplementer.h"
#include "DTCHAR.h"
#include "CFileModule.h"
#include "CRegexModule.h"
#include "CExceptionMod.h"
#include "CoreFunctions.h"

///////////////////////////////////
#if defined(USING_MFC) && defined(_DEBUG)
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///////////////////////////////////

//////////////////////////////////////////////////////
// These are the core module headers registered in the
// CInterpreter constructor using RegisterCoreModule()
#include "CExceptionMod.h"
#include "CSocketModule.h"
#include "MathModule.h"
//#include "Copy of MathModule.h"
#if defined(__WINNT__) || defined(__WINCE__)
#	include "COutputModule.h"
#	include "Pfrog.h"
#	include "GUIModule.h"
#	include "SystemModule.h"
#	ifdef ADO_SUPPORT
#		include "ADOModule.h"
#	endif
#	ifdef CRYPTO_SUPPORT
#		include "CryptoModule.h"
#	endif
#endif
//////////////////////////////////////////////////////

#pragma warning(disable:4509)	// SEH and C++ destructor warning (clean-up sometime)

#define m_Token		(*(m_DataStack.CodeStream()))

extern TString TokenTypeToKeywordString(TokenTypes tok);
VALUE g_vCmdLineArray = AllocArray(0); // For extern set by app init
VALUE g_vCmdLineString;

//////////////////////////////////////////////////////
// Use these macros to help let exceptions through

#ifdef USE_SEH
#define RETURN __leave
#else
#define RETURN return
#endif

bool CDialectInterp::IntExpression(int& result)
{
	VALUE vTemp;
	if (!Expression(vTemp)) return false;
	if (vTemp.Type() != stInteger) {
		RuntimeError(_T("Expression did not reduce to integer as required"));
		return false;
	}
	result = vTemp.Integer();
	return true;
}

//==========================================================
// Help for validating arguments in core functions...
/*LPCTSTR SymbolTypeToString(SymbolTypes type)
{
	switch (type) {
		case stUndefined:		return _T("undefined");
		case stNil:				return _T("nil");
		case stString:			return _T("string");
		case stFloat:			return _T("float");
		case stInteger:			return _T("integer");
		case stBoolean:			return _T("boolean");
		case stArray:			return _T("array");
		case stFrame:			return _T("frame");
		case stClass:			return _T("class");
		case stClassInstance:	return _T("classinstance");
		case stFunction:		return _T("function");
		case stSymbol:			return _T("symbol");
		case stProperty:		return _T("property");
		case stVoid:			return _T("void");
		case stChunk:			return _T("chunk");
		default:				return _T("unknown");
	}
}*/

/*bool ValidateArgs(int count, SymbolTypes types[], int argCount, VALUE args[], CInterpreter* pInterp)
{
	if (argCount != count) {
		pInterp->SyntaxError(
			_T("Invalid number of args in function call. Expected: %d, received: %d"),
			count, argCount);
		return false;
	}
	else {
		for (int idx = 0; idx < count; ++idx) {
			// types[idx] == stUndefined means we don't care about the type...
			if ((types[idx] != stUndefined) && (args[idx].Type() != types[idx])) {
				TString error;
				error.Format(_T("Invalid type for argument %d. Expected: %s, received: %s"),
					idx, SymbolTypeToString(types[idx]), SymbolTypeToString(args[idx].Type()));
				pInterp->RuntimeError(error);
				return false;
			}
		}
	}
	return true;
}*/

////////////////////////////////////////////////////////////////

class CDefaultInImpl : public CClassImpl {
public:
	CDefaultInImpl();
	~CDefaultInImpl();
};

class CDefaultOutImpl : public CClassImpl {
public:
	CDefaultOutImpl();
	~CDefaultOutImpl();
};

static VALUE DefaultInReadFunc(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	CDialectInterp *pDInterp = static_cast<CDialectInterp*>(pInterp);
	TString (*pReadFunc)(int count) = pDInterp->m_pDefaultReadFunc;
	if (pReadFunc != NULL) {
		if ((argCount == 0) || ((argCount == 1) && (args[0].Type() == stNil))) {
			return pReadFunc(-1);		// Read all data
		}
		else if ((argCount == 1) && (args[0].Type() == stInteger)) {
			return pReadFunc(args[0].Integer());	// Read requested amount of data
		}
		pInterp->RuntimeError(_T("defaultin.read requires zero or one integer argument"));
	}
	return _T("");
}

static VALUE DefaultInReadLineFunc(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	CDialectInterp *pDInterp = static_cast<CDialectInterp*>(pInterp);
	TString (*pReadLineFunc)() = pDInterp->m_pDefaultReadLineFunc;
	if (pReadLineFunc != NULL) {
		if (argCount == 0) {
			return pReadLineFunc();
		}
		else {
			pInterp->RuntimeError(_T("defaultin.readline requires zero arguments"));
		}
	}
	return _T("");
}

static VALUE DefaultOutWriteFunc(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	CDialectInterp *pDInterp = static_cast<CDialectInterp*>(pInterp);
	if (argCount != 1) {
		pInterp->RuntimeError(_T("defaultout.write requires exactly one argument"));
	}
	else if (pDInterp->m_pDefaultWriteFunc != NULL) {
		pDInterp->m_pDefaultWriteFunc(args[0].String());
	}
	return AllocNil();
}

static VALUE DefaultOutWriteLineFunc(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	CDialectInterp *pDInterp = static_cast<CDialectInterp*>(pInterp);
	if (pDInterp->m_pDefaultWriteLineFunc != NULL) {
		if (argCount == 0) {
			pDInterp->m_pDefaultWriteLineFunc(_T(""));
		}
		else if (argCount == 1) {
			pDInterp->m_pDefaultWriteLineFunc(args[0].String());
		}
		else {
			pInterp->RuntimeError(_T("defaultout.writeline requires zero or one argument"));
		}
	}
	return AllocNil();
}

////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////

CDialectInterp::CDialectInterp()
{
	m_BreakPointFunc = NULL;
	m_DebugOutFunc = NULL;
	m_BackgroundHandler = NULL;
	m_BackgroundHandlerFreq = 0;
	m_BackgroundHandlerFreqCount = 0;

	m_RegCoreModules = AllocFrame(31);	// Don't clear in Reset()
#if defined(__WINNT__) || defined(__WINCE__)
	RegisterCoreModule(OUTPUT_MODULE_NAME, InitOutputModule);
	RegisterCoreModule(GUI_MODULE_NAME, InitGUIModule);
	RegisterCoreModule(SYSTEM_MODULE_NAME, InitSystemModule);
	RegisterCoreModule(FROG_MODULE_NAME, InitFrogModule);
	//RegisterCoreModule(MATHY_MODULE_NAME,InitMathyModule);
	#ifdef ADO_SUPPORT
		RegisterCoreModule(ADO_MODULE_NAME, InitADOModule);
	#endif
	#ifdef CRYPTO_SUPPORT
		RegisterCoreModule(CRYPTO_MODULE_NAME, InitCryptoModule);
	#endif
#endif
	RegisterCoreModule(SOCKET_MODULE_NAME, InitSocketModule);
	RegisterCoreModule(EXCEPTION_MODULE_NAME, InitExceptionModule);
#ifdef MATH_MODULE_SUPPORT
	RegisterCoreModule(MATH_MODULE_NAME, InitMathModule);
#endif

	m_pDefaultReadFunc = NULL;
	m_pDefaultReadLineFunc = NULL;
	m_vDefaultInInst = AllocClassInstance(AllocClass(new CDefaultInImpl()), this);
	m_vDefaultInInst.ClassInstance()->m_SelfSymTab.Set(_T("read"), AllocFunction(DefaultInReadFunc, _T("")));
	m_vDefaultInInst.ClassInstance()->m_SelfSymTab.Set(_T("readline"), AllocFunction(DefaultInReadLineFunc, _T("")));

	m_pDefaultWriteFunc = NULL;
	m_pDefaultWriteLineFunc = NULL;
	m_vDefaultOutInst = AllocClassInstance(AllocClass(new CDefaultOutImpl()), this);
	m_vDefaultOutInst.ClassInstance()->m_SelfSymTab.Set(_T("write"), AllocFunction(DefaultOutWriteFunc, _T("")));
	m_vDefaultOutInst.ClassInstance()->m_SelfSymTab.Set(_T("writeline"), AllocFunction(DefaultOutWriteLineFunc, _T("")));

	Reset();
}

CDialectInterp::~CDialectInterp()
{
	// Although, technically this might not be necesaryy, if any of
	// these symbol tables contains a class instance, the destructor
	// is going to attempt to execute.  We need to make sure that
	// happens BEFORE the interpreter destructor is finished.  Hence
	// the attempted forced garbage collection tactics.  Set the frames
	// to empty frames because the destructor execution may attempt to
	// call get() on a frame, which will cause an internal error if
	// the value is, say, set to VALUE().
	// This order is somewhat important...
	m_vOutputDev = VALUE();
	m_vException = VALUE();
	m_SpecialSymTab = AllocFrame();
	m_SymbolTables = AllocFrame();
	m_GlobalSymTab = AllocFrame();
}

void CDialectInterp::Reset()
{
	// Only to be called with an empty call stack
	// Such as during contructor of CInterpreter or after a succesful Abort()
	m_vOutputDev = VALUE();
	m_WriteFunc = NULL;
	m_TabFunc = NULL;
	m_CRFunc = NULL;
	m_TableFunc = NULL;
	m_LinesLeftFunc = NULL;
	m_SectItemFunc = NULL;
	m_StartSectFunc = NULL;
	m_EndSectFunc = NULL;

#ifdef DIALECT_LITE
	m_ImportCount = 0;
#endif

	m_bInBreakpoint = false;	// More breakpoint stuff
	m_bReturning = RETURN_NOT;
	m_vException = AllocNil();

	m_GlobalSymTab = AllocFrame(51);
	m_GlobalSymTab.Set(_T("__environ__"), AllocFrame(31));	// Stores command line, etc.

	TCHAR buf[MAX_PATH];
	GET_EXE_FILENAME(buf, MAX_PATH);
	SetEnvironVar(_T("exe"), buf);
	if (g_vCmdLineArray.Type() == stArray) {
		SetEnvironVar(_T("cmdline"), g_vCmdLineArray);
	}
	else {
		SetEnvironVar(_T("cmdline"), AllocArray(0));
	}
	SetEnvironVar(_T("cmdline$"), g_vCmdLineString);
	//SetEnvironVar(_T("printerName"), _T(""));
	SetEnvironVar(_T("dataDir"), _T(""));	// Delete me (scripts should look at cmdline)
	SetScriptPath(m_InitialPath);
	//SetEnvironVar(_T("version$"), (LPCTSTR)__VERSION_STRING__);
	SetEnvironVar(_T("version"), (float)__VERSION_NUMBER__);
	SetEnvironVar(_T("os$"), (LPCTSTR)__OPERATING_SYS__); // Delete me
	SetEnvironVar(_T("os"), (LPCTSTR)__OPERATING_SYS__);
#ifdef UNDER_CE
	SetEnvironVar(_T("ceversion"), (int)UNDER_CE);
#	ifdef WIN32_PLATFORM_PSPC
	SetEnvironVar(_T("ceplatform"), _T("PocketPC"));
#	else
	SetEnvironVar(_T("ceplatform"), _T("HPC"));
#	endif
#endif

	// Let's also init some special items
	m_SpecialSymTab = AllocFrame(97);	// For things like the file class
	m_SpecialSymTab.Set(_T("__module__"), _T("__core__"));
	InitCoreFuncs(m_SpecialSymTab.Frame());
	m_SpecialSymTab.Set(_T("file"), AllocProperty(AllocNil(),
		AllocFunction(CantWriteMe, _T("")), AllocClass(new CFileImpl())));
	m_SpecialSymTab.Set(_T("regex"), AllocProperty(AllocNil(),
		AllocFunction(CantWriteMe, _T("")), AllocClass(new CRegexImpl())));
	m_SpecialSymTab.Set(_T("output"), AllocProperty(AllocNil(),
		AllocFunction(WriteOutputDev, _T("")), m_vDefaultOutInst));
	m_SpecialSymTab.Set(_T("input"), AllocProperty(AllocNil(),
		AllocNil(), m_vDefaultInInst));
	m_SpecialSymTab.Set(_T("__defout__"), AllocProperty(AllocNil(),
		AllocFunction(CantWriteMe, _T("")), m_vDefaultOutInst));
	m_SpecialSymTab.Set(_T("__defin__"), AllocProperty(AllocNil(),
		AllocFunction(CantWriteMe, _T("")), m_vDefaultInInst));

	// Because the interp breaks out the output functions for speed,
	// we need to force the call to WriteOutputDev the first time...
	VALUE vTemp[2];
	vTemp[0] = AllocNil();
	vTemp[1] = m_vDefaultOutInst;
	WriteOutputDev(NULL, 2, vTemp, this);

	m_SymbolTables = AllocFrame(37);
	m_SymbolTables.Set(_T("__core__"), m_SpecialSymTab);

	m_OpenWindows = 0;	// Should be true since everything was garbage collected
}

long CDialectInterp::IncOpenWindows()
{
	return INTERLOCKED_INC(m_OpenWindows);
}

long CDialectInterp::DecOpenWindows()
{
	return INTERLOCKED_DEC(m_OpenWindows);
}

long CDialectInterp::GetOpenWindows() const
{
	return m_OpenWindows;
}

int CDialectInterp::GetStackLevel() const
{
	return m_DataStack.Level();
}

void CDialectInterp::Error(TString title, TString message)
{
	//MessageBox(NULL, (LPCTSTR)message, (LPCTSTR)title, MB_OK);
}

void CDialectInterp::SetEnvironVar(LPCTSTR key, VALUE value)
{
	ASSERT(m_GlobalSymTab.Type() == stFrame);
	VALUE vEnviron = m_GlobalSymTab.Get(_T("__environ__"));
	ASSERT(vEnviron.Type() == stFrame);
	vEnviron.Set(key, value);
}

bool CDialectInterp::InBreakpoint()
{
	return m_bInBreakpoint;
}

VALUE CDialectInterp::CallStack()
{
	return m_DataStack.CallStack();
}

void CDialectInterp::Abort()
{
	// Tell interp to completely unwind stack
	m_bReturning = RETURN_ABORT;
}

void CDialectInterp::SetDebugOutFunc(void (*debugOutFunc)(TString text))
{
	m_DebugOutFunc = debugOutFunc;
}

void CDialectInterp::SetBreakPointFunc(void (*breakPointFunc)(TString module, int line, CInterpreter* pInterp))
{
	m_BreakPointFunc = breakPointFunc;
}

void CDialectInterp::SetBackgroundHandler(int frequency, void (*backgroundFunc)(CInterpreter* pInterp))
{
	m_BackgroundHandler = backgroundFunc;
	m_BackgroundHandlerFreq = frequency;
	m_BackgroundHandlerFreqCount = 0;
}

int CDialectInterp::CurrentLine()
{
	if ((m_DataStack.Level() >= 0) &&
		(m_DataStack.CodeStream() != NULL) &&
		!m_Token.ModuleName().IsEmpty())
	{
		return m_Token.LineNumber();
	}
	else return 0;
}

TString CDialectInterp::CurrentModuleName()
{
	// The easiest and most common way first...
	if ((m_DataStack.Level() >= 0) &&
		(m_DataStack.CodeStream() != NULL) &&
		!m_Token.ModuleName().IsEmpty())
	{
		return m_Token.ModuleName();
	}

	CFrameImpl* pMod = m_DataStack.ModuleSymTab();
	if (pMod == NULL) return _T("__core__");

	TString module = pMod->__get__(_T("__module__")).String();
	if (module.IsEmpty()) module = _T("__core__");
	return module;
}

TString CDialectInterp::CurrentModulePath()
{
	// The easiest and most common way first...
	if ((m_DataStack.Level() >= 0) &&
		(m_DataStack.CodeStream() != NULL) &&
		!m_Token.ModulePath().IsEmpty())
	{
		return m_Token.ModulePath();
	}

	CFrameImpl* pMod = m_DataStack.ModuleSymTab();
	if (pMod == NULL) return _T("");
	else return pMod->__get__(_T("__path__")).String();
}

VALUE CDialectInterp::CurrentModuleFrame()
{
	CFrameImpl* pFrame = m_DataStack.ModuleSymTab();
	if (pFrame == NULL) return AllocFrame();

	// We could theoretically coerce the CFrameImpl pointer into
	// a VALUE using VALUE::ForceCI(), but seems risky in terms
	// of garbage collection, etc.  Let's just get it via name
	// from the frame that stores all the module frames.

	TString name = pFrame->__get__(_T("__module__")).String();
	VALUE vResult = m_SymbolTables.Get(name);
	if (vResult.Type() != stFrame) {
		ASSERT(false);	// This should never happen!
		vResult = AllocFrame();
	}
	return vResult;
}

VALUE CDialectInterp::RequestModule(TString moduleName)
{
	// A derived class would return the module's frame if
	// it implements the requested module.  All derived class
	// should call there parent's version of RequestModule if
	// they don't implement the module.

	// Let's see if it was registered using RegisterCoreModule...
	VALUE vpInitFunc = m_RegCoreModules.Get(moduleName);
	if ((vpInitFunc.Type() == stInteger) && (vpInitFunc.Integer() != 0)) {
		VALUE (*pInitFunc)() = (VALUE (*)())(vpInitFunc.Integer());
		VALUE module = (*pInitFunc)();
		ASSERT(module.Type() == stFrame);
		module.Set(_T("__module__"), moduleName);	// Just to be sure
		return module;
	}

	return VALUE();
}

void CDialectInterp::RegisterCoreModule(TString moduleName, VALUE (*initFunc)())
{
	ASSERT(!moduleName.IsEmpty());
	ASSERT(initFunc != NULL);
	m_RegCoreModules.Set(moduleName, (int)initFunc);
}

bool CDialectInterp::LoadScript(CByteCode* cs, TString& moduleName)
{
	// Load a script into the given CByteCode object
	// Check to see if an up to date compiled version exist,
	// if it does, load it, else load the ascii version and
	// compile it, then save it for later

	// Dialect Lite isn't allowed to create .dc files
#ifdef DIALECT_LITE
#	define SAVE_DC_FILE(f)		true
#else
#	define SAVE_DC_FILE(f)		cs->SaveToFile(f)
#endif

	// Check to see if it was appended to the executable...
	if (GetAppendedScript(cs, moduleName)) return true;

	TString appdir;
	TCHAR* buf = appdir.GetBuffer(MAX_PATH);
	GetModuleFileName(NULL, buf, MAX_PATH);
	appdir.ReleaseBuffer();
	int p = appdir.ReverseFind('\\');
	if (p > 0) {
		appdir = appdir.Left(p + 1);
	}

	TString ASCIIScript;
	TString Precompiled;
	for (int idx = -1; idx < m_PathCount; ++idx) {
		if (idx == -1) {
			// Try the interpreter's local directory first
			ASCIIScript = appdir + moduleName + lpszScriptExt;
			Precompiled = appdir + moduleName + lpszPrecompExt;
		}
		else {
			ASCIIScript = m_ScriptPath[idx] + moduleName + lpszScriptExt;
			Precompiled = m_ScriptPath[idx] + moduleName + lpszPrecompExt;
		}

		//MessageBox(NULL, ASCIIScript + "\r\n" + Precompiled, "", MB_OK);

		if (TFile::Exists(Precompiled)) {
			if (TFile::Exists(ASCIIScript) && TFile::NewerThan(ASCIIScript, Precompiled)) {
				// An ACSII version of the script exists and is newer than
				// the precompiled version, so we need to recompile and save it
				if (!cs->CompileFile(ASCIIScript) || !SAVE_DC_FILE(Precompiled)) {
					RuntimeError(cs->GetLastError());
					return false;
				}
			}
			else {
				if (!cs->LoadFromFile(Precompiled)) {
					RuntimeError(cs->GetLastError());
					return false;
				}
			}
			return true;
		}
		else if (TFile::Exists(ASCIIScript)) {
			// Load and compile the module
			// Then, save the compiled version for later runs
			if (!cs->CompileFile(ASCIIScript) || !SAVE_DC_FILE(Precompiled)) {
				RuntimeError(cs->GetLastError());
				return false;
			}
			return true;
		}
	}

	RuntimeError(_T("Unable to locate script: %s"), (LPCTSTR)moduleName);
	return false;
}

bool CDialectInterp::CompileFile(TString fileIn, TString fileOut, TString& exception)
{
	CByteCode cs;
	exception.Empty();

	if (!TFile::Exists(fileIn)) {
		exception = _T("File does not exist: ") + fileIn;
	}
	else if (!cs.CompileFile(fileIn)) {
		exception = cs.GetLastError();
	}
	else if (!cs.SaveToFile(fileOut)) {
		exception = cs.GetLastError();
	}
	else {
		return true;
	}
	return false;
}

bool CDialectInterp::Interpret(TokenTypes breakOnTok1,
							 TokenTypes breakOnTok2,
							 TokenTypes breakOnTok3,
							 TokenTypes breakOnTok4,
							 TokenTypes breakOnTok5)
{
	//TCHAR error[512];

	int StackLevel = m_DataStack.Level();	// Just in case of exception, we reset

	_TRY {
		bool bDone = false;
		while (((TokenTypes)m_Token != breakOnTok1) && ((TokenTypes)m_Token != breakOnTok2)
			&& ((TokenTypes)m_Token != breakOnTok3) && ((TokenTypes)m_Token != breakOnTok4)
			&& ((TokenTypes)m_Token != breakOnTok5)){

			// Allow message pumps, etc...
			if ((m_BackgroundHandler != NULL) &&
				(m_BackgroundHandlerFreq > 0) &&
				(++m_BackgroundHandlerFreqCount >= m_BackgroundHandlerFreq))
			{
				m_BackgroundHandlerFreqCount = 0;
				m_BackgroundHandler(this);
				if ((m_bReturning != RETURN_NOT) && (m_bReturning != RETURN_FUNCTION)) {
					// Abort was called while we were in the background handler
					// Stop execution before we get to the following switch statement...
					return false;
				}
			}

			// We are checking m_bInBreakpoint because if something is interpreted while
			// we are in a breakpoint condition, it must be the user doing some debug poking
			// and we DON'T want to break on a debug windows code
			if (!m_bInBreakpoint && (m_BreakPointFunc != NULL) && ((TokenTypes)m_Token != tokEOL)) {
				m_bInBreakpoint = true;
				m_BreakPointFunc(CurrentModuleName(), CurrentLine(), this);
				m_bInBreakpoint = false;
				if ((m_bReturning != RETURN_NOT) && (m_bReturning != RETURN_FUNCTION)) {
					// Abort was called while we were in the breakpoint
					// Stop execution before we get to the following switch statement...
					return false;
				}
			}

			switch ((TokenTypes)m_Token) {
				case tokEOL:		++m_Token;			break;
				case tokIdent:		DoAssignment();		break;
				case tokIf:			DoIfThenElse();		break;
				case tokFor:		DoForLoop();		break;
				case tokWhile:		DoWhileLoop();		break;
				case tokPrint:		DoPrint();			break;
				case tokSItem:		DoSItem();			break;
				case tokStartSect:	DoStartSect();		break;
				case tokEndSect:	DoEndSect();		break;
				case tokWith:		DoWith();			break;
				case tokDot:		DoAssignment();		break;			// Must be in a 'with' block
				case tokFunc:		DoFuncDef();		break;
				case tokForEach:	DoForEach();		break;
				case tokLocal:		DoLocal();			break;
				case tokImport:		DoImport();			break;
				case tokReturn:		DoReturn();			bDone = true; break;
				case tokTry:		DoTry();			break;
				case tokThrow:		DoThrow();			bDone = true; break;	// "returns" an exception
				case tokRun:		DoRun();			break;
				case tokOpenBracket: DoArrayAssignment();	break;
				case tokClass:		DoClass();			break;
				case tokInterpret:	DoInterpret();		break;
				case tokInherited:	DoAssignment();		break;
				case tokEOF:		bDone = true;		break;
				case tokPersistComment:	DoPersistComment();		break;
				case tokDebugOut:	DoDebugOut();		break;
				case tokSemicolon:	++m_Token;			break;	// Semicolon like tokEOL
				case tokBackslash:	++m_Token;
					Require(tokEOL, _T("Missing EOL after line continuation token '\'"));
					break;
				case tokUndefined:	++m_Token;			break;
				default:
					SyntaxError(_T("Expecting a statement, unexpected token: ") + TokToString(m_Token));
					bDone = true;
			}
			if (m_bReturning) bDone = true;	// We are returning from a function call, so keep going
			if (bDone) break;	// Break while loop
		}
	}
	_CATCH_EXCEPTION {
		DWORD code = GET_EXC_CODE;
		LPCTSTR msg = GET_EXC_MESSAGE;

		// If an C++ exception is caught, convert it into a Dialect exception...
		switch (code) {
			case EXCEPTION_SYNTAX:
				//_stprintf(error, _T("%s: %s"), _T("Syntax error"), (LPCTSTR)exc.message);
				SetException(_T("esyntax"), msg, CurrentModuleName(), CurrentLine(), AllocNil());
				break;
			case EXCEPTION_RUNTIME:
				//_stprintf(error, _T("%s: %s"), _T("Runtime error"), (LPCTSTR)exc.message);
				SetException(_T("eruntime"), msg, CurrentModuleName(), CurrentLine(), AllocNil());
				break;
			case EXCEPTION_INTERPRETER:
				//_stprintf(error, _T("%s: %s"), _T("Internal error"), (LPCTSTR)exc.message);
				SetException(_T("einternal"), msg, CurrentModuleName(), CurrentLine(), AllocNil());
				break;
			default:
				SetException(_T("einternal"), _T("*Unkown internal error"), CurrentModuleName(), CurrentLine(), AllocNil());
		}
	}
/*#ifndef USE_SEH
	catch(...) {
		// C++ exception handling will only catch EException class exceptions above
		// because of the we the macro is written, so we need this catch all handler
		// to avoid NTS (Nasty Temination Syndrom)
		SetException(_T("einternal"), _T("Unhandled internal error"), m_Token.m_ParentModule, m_Token.LineNumber(), AllocNil());
	}
#endif*/

	m_DataStack.PopToLevel(StackLevel);
	return ((m_bReturning == RETURN_NOT) || (m_bReturning == RETURN_FUNCTION));
}

void CDialectInterp::Unserialize(TString filename, VALUE& result)
{
	// tokUnserialize already eaten (this function called by expression parser)
	result.SerializeIn(filename);
}

long CDialectInterp::SkipBlock(TokenTypes headTok, TokenTypes tailTok,
							 TokenTypes otherTok1, TokenTypes otherTok2)
{
	/*
	 *	Pass through everything between headTok and tailTok
	 *	Make sure that we don't stop at the tailToks that aren't
	 *	really the correct match.
	 *	while idx > 0		// This while is the headTok
	 *		idx = idx + 1
	 *		while loop == true:
	 *			loop = false
	 *		endwhile		// Don't stop here
	 *	endwhile			// Do stop here
	 */
	int startLine = CurrentLine();
	int depth = 1;
	while (depth > 0) {
		++m_Token;
		if ((TokenTypes)m_Token == headTok) {
			depth++;
		}
		else if ((depth == 1) && ((TokenTypes)m_Token == otherTok1)) {
			// This is most likely an tokElseIf
			depth = 0;
		}
		else if ((depth == 1) && ((TokenTypes)m_Token == otherTok2)) {
			// Or maybe an tokElse
			depth = 0;
		}
		else if ((TokenTypes)m_Token == tailTok) {
			depth--;
		}
		else if ((TokenTypes)m_Token == tokEOF) {
			SyntaxError(_T("Hit EOF while trying to find '") + TokenTypeToString(tailTok) +
				_T("' for block starting near line %d"), startLine);
			break;
		}
	}
	return m_Token.Position();
}

void CDialectInterp::DoInterpret()
{
	++m_Token;
	VALUE vTemp;
	if (Expression(vTemp)) {
		TString excText;
		if (!DebugInterpretText(vTemp.String(), excText)) {
			RuntimeError(_T("interpret statement failed: ") + excText);
		}
	}
}

void CDialectInterp::DoRun()
{
	CByteCode cs;
	TString moduleName, filename, ASCIIScript, Precompiled;
	VALUE vTemp, _result, modSymTab;

	++m_Token;	// Eat tokRun
	if (!Expression(vTemp)) return;
	filename = vTemp.String();
	moduleName = ModuleNameFromFilename(filename);

	// Let's not let folks run a module that is stored as a core module
	// Not that they could, but we don't want an ascii script to run
	// using the same name as one of our core modules.  Would create confussion.
	if (RequestModule(moduleName).Type() == stFrame) {
		RuntimeError(_T("Can't run a core module: %s"), (LPCTSTR)moduleName);
		return;
	}

	// Create the modules private symbol table
	// We don't store it permanently when doing tokRun (as we do on import)
	modSymTab = AllocFrame();

	VALUE vArgs[16];
	int argCount = 0;
	while ((TokenTypes)m_Token == tokComma) {
		++m_Token;
		if (argCount >= 16) {
			SyntaxError(_T("Exceeded max of 16 arguments in import statement"));
		}
		// Get arguments to pass to the script
		if (!Expression(vArgs[argCount++])) return;
	}
	modSymTab.Set(_T("__args__"), AllocArray(argCount, vArgs));

	// All module symbol tables contain their names in __module__ variable
	modSymTab.Set(_T("__module__"), moduleName);

	// Running a script now can return a value in #return#
	// !!!  If we setup this function in a way to retrieve this value
	// !!!  Unless you can tell otherwise, this isn't yet implemented
	modSymTab.Set(_T("#result#"), AllocNil());

	if (LoadScript(&cs, moduleName)) {
		int StackLevel = m_DataStack.Level();
		m_SymbolTables.Set(moduleName, modSymTab); // Set for duration of run only!
		m_DataStack.Push(modSymTab.Frame(), NULL, &cs);
		Interpret();
		m_DataStack.PopToLevel(StackLevel);
		m_SymbolTables.Remove(moduleName);	// Run's don't permanently store symtab!
		if (m_bReturning == RETURN_FUNCTION) m_bReturning = RETURN_NOT;
	}
}

void CDialectInterp::DoPersistComment()
{
	// As with a quoted string, m_Token.m_StringResult contains the comments text
	++m_Token;	// Eat tokPersistComment (and contained comment string)
}

void CDialectInterp::DoLocal()
{
	++m_Token;	// Eat tokLocal

	if (m_DataStack.FunctionSymTab() == NULL) {
		RuntimeError(_T("The 'local' statement is only valid in a function"));
		return;
	}

	while (true) {
		switch ((TokenTypes)m_Token) {
			case tokComma:
				++m_Token;
				break;
			case tokIdent:
				{
					VALUE val = AllocNil();
					TString name = (LPCTSTR)m_Token;
					++m_Token;
					if ((TokenTypes)m_Token == tokAssignment) {
						++m_Token;
						if (!Expression(val)) return;
					}
					m_DataStack.FunctionSymTab()->__set__(name, val);
				}
				break;
			case tokBackslash:
				++m_Token;
				if (!Require(tokEOL, _T("Missing EOL after line continuation token '\'")))
					return;
				break;
			case tokEOF:
			case tokEOL:
			case tokSemicolon:
				return;
			default:
				SyntaxError(_T("Invalid token in local statement: %s"),
					(LPCTSTR)TokToString(m_Token));
				return;
		}
	}
}

TString CDialectInterp::ModuleNameFromFilename(TString filename)
{
	TString result = filename;
	/*
	 *	filename is something like "C:\\This is a test\\of something\\testcode.txt"
	 *	and we want to extract just "testcode".  For Unix compatibility we would also
	 *	need to check for '/'
	 */
	result = result.Right(result.GetLength() - result.ReverseFind(DIR_SEPARATOR) - 1);
	int ext = result.ReverseFind('.');
	if (ext > 0) result = result.Left(ext);
	return result;
}

static void AddAs(CFrameImpl* AddToThis, TString& as, VALUE& moduleToAdd)
{
	ASSERT(AddToThis != NULL);

	if (as.IsEmpty()) {
		SyntaxError(_T("import as may not be followed by an empty string"));
		return;
	}
	else if (as[0] == '*') {
		// Copy member by member
		FindNextInfo fni;
		VALUE key, value;
		while (moduleToAdd.FindNext(fni, key, value)) {
			// We might possibly want to rule out all __xxx__ style values, but for now...
			if (key.String().CompareNoCase(_T("__module__")) != 0) {
				AddToThis->Add(key.String(), value);
			}
		}
	}
	else {
		AddToThis->Add(as, moduleToAdd);
	}
}

void CDialectInterp::DoImport()
{
	CByteCode cs;
	TString filename, moduleName, as;

	++m_Token;	// Eat tokImport
	if ((TokenTypes)m_Token != tokQuotedString) {
		SyntaxError(_T("Invalid import statement."));
		return;
	}

	filename = (LPCTSTR)m_Token;
	++m_Token;		// Eat module's filename string
	moduleName = ModuleNameFromFilename(filename);

	as = moduleName;	// The default import key is the actual module name
	if ((TokenTypes)m_Token == tokAs) {
		++m_Token;
		switch ((TokenTypes)m_Token) {
			case tokIdent:
			case tokQuotedString:
				as = (LPCTSTR)m_Token;
				break;
			case tokTimes:
				as = "*";
				break;
			default:
				SyntaxError(_T("Invalid argument after 'as' in import statement."));
				return;
		}
		++m_Token;
	}

	// First check to see if the module is implemented inside the interpreter

	/*
	 *	Check to see if the module has been loaded already, if not
	 *	load into a compiled stream.  Create a new symbol table for
	 *	it and place it on the symbol table stack, push code stream,
	 *	then interpret the compiled code to generate the symbols in the file.
	 *	Remove the symbol table and store it as the modules symbol table in m_SymbolTables.
	 */
	VALUE modSymTab = m_SymbolTables.Get(moduleName);

	if (modSymTab.Type() != stUndefined) {
		// It's already been imported, so just add it's reference to the
		// current locals symbol table
		if (m_DataStack.FunctionSymTab() != NULL) {
			AddAs(m_DataStack.FunctionSymTab(), as, modSymTab);
		}
		else if (m_DataStack.ModuleSymTab() != NULL) {
			AddAs(m_DataStack.ModuleSymTab(), as, modSymTab);
		}
	}
	else {
		// First, check to see if the module is a C++ core module...
		modSymTab = RequestModule(moduleName);
		if (modSymTab.Type() == stFrame) {
			m_SymbolTables.Set((LPCTSTR)moduleName, modSymTab);
			// Add it to the local modules name space
			if (m_DataStack.FunctionSymTab() != NULL) {
				AddAs(m_DataStack.FunctionSymTab(), as, modSymTab);
			}
			else if (m_DataStack.ModuleSymTab() != NULL) {
				AddAs(m_DataStack.ModuleSymTab(), as, modSymTab);
			}
		}
		else {
			// We didn't find a core module with this name...
			// Create the modules private symbol table
			// <<Don't store it until we after the import is complete>>
			// Changing that to store it, but remove if exception occured
			modSymTab = AllocFrame();

			VALUE vArgs[16];
			int argCount = 0;
			while ((TokenTypes)m_Token == tokComma) {
				++m_Token;
				if (argCount >= 16) {
					SyntaxError(_T("Exceeded max of 16 arguments in import statement"));
				}
				// Get arguments to pass to the script
				if (!Expression(vArgs[argCount++])) return;
			}
			modSymTab.Set(_T("__args__"), AllocArray(argCount, vArgs));

			// All module symbol tables contain there names in __module__ variable
			modSymTab.Set(_T("__module__"), moduleName);

			// Importing a module can return a value in #return#
			// !!!  If we setup this function in a way to retrieve this value
			// !!!  Unless you can tell otherwise, this isn't yet implemented
			modSymTab.Set(_T("#result#"), AllocNil());

			if (LoadScript(&cs, moduleName)) {
				int StackLevel = m_DataStack.Level();
				m_SymbolTables.Set(moduleName, modSymTab);
				m_DataStack.Push(modSymTab.Frame(), NULL, &cs);
				Interpret();
				m_DataStack.PopToLevel(StackLevel);
				if (m_bReturning == RETURN_FUNCTION) {
					m_bReturning = RETURN_NOT;
				}
				else if (m_bReturning == RETURN_EXCEPTION) {
					m_SymbolTables.Remove(moduleName);
				}

				if (m_bReturning == RETURN_NOT) {
					// Add it to the local modules name space
					if (m_DataStack.FunctionSymTab() != NULL) {
						AddAs(m_DataStack.FunctionSymTab(), as, modSymTab);
					}
					else if (m_DataStack.ModuleSymTab() != NULL) {
						AddAs(m_DataStack.ModuleSymTab(), as, modSymTab);
					}
				}
			}
		}
	}
}

void CDialectInterp::DoForLoop()
{
	/*
	 *	for idx = 1 to 10
	 *	...
	 *	endfor
	 */
	++m_Token;	// Eat tokFor

	if ((TokenTypes)m_Token != tokIdent) {
		SyntaxError(_T("For loop variable must be an identifier."));
		return;
	}
	VALUE* pVal;
	pVal = FindValuePtr((LPCTSTR)m_Token, true);
	++m_Token;

	if (!Require(tokAssignment, _T("Missing '=' in for loop statement."))) return;

	int first, last;
	if (!IntExpression(first)) return;
	if (!Require(tokTo, _T("Missing 'to' in for loop statement."))) return;
	if (!IntExpression(last)) return;

	int startLineNum = m_Token.LineNumberNoOffset();
	long startpos = m_Token.Position() - 1;
	long endpos = SkipBlock(tokFor, tokEndFor);
	int endLineNum = m_Token.LineNumberNoOffset();

	m_Token.Seek(startpos, startLineNum);

	*pVal = first;
	while (first <= last) {
		// Update counting variable (force single stepping for now)
		*pVal = first++;

		Interpret(tokEndFor, tokContinue, tokBreak);
		if (m_bReturning) break;	// We are returning from a function call, so keep going
		if ((TokenTypes)m_Token == tokBreak) {
			++m_Token;
			break;
		}
		m_Token.Seek(startpos, startLineNum);
	}
	m_Token.Seek(endpos, endLineNum);
}

void CDialectInterp::DoForEach()
{
	VALUE* keyVal = NULL;
	VALUE* valueVal = NULL;

	/*
	 *	foreach x in ["a", "b", "c"]
	 *	foreach key, value in someDictionaryVar
	 *	...
	 *	next
	 */
	++m_Token;	// Eat tokForEach

	if ((TokenTypes)m_Token != tokIdent) {
		SyntaxError(_T("Foreach loop variable must be an identifier."));
		return;
	}
	keyVal = FindValuePtr((LPCTSTR)m_Token, true);
	++m_Token;

	if ((TokenTypes)m_Token == tokComma) {
		++m_Token;
		valueVal = FindValuePtr((LPCTSTR)m_Token, true);
		++m_Token;
	}

	if (!Require(tokIn, _T("Missing 'in' in foreach statement"))) return;

	VALUE source;
	if (!Expression(source)) return;

	int startLineNum = m_Token.LineNumberNoOffset();
	long startpos = m_Token.Position() - 1;
	long endpos = SkipBlock(tokForEach, tokEndForEach);
	int endLineNum = m_Token.LineNumberNoOffset();

	m_Token.Seek(startpos, startLineNum);

	FindNextInfo fni;
	while (true) {
		// Update variable
		if (valueVal == NULL) {
			source.FindNext(fni, *keyVal);
		}
		else {
			source.FindNext(fni, *keyVal, *valueVal);
		}

		if (fni.info == FIND_NEXT_FINISHED) break;	// Nothing left

		Interpret(tokEndForEach, tokContinue, tokBreak);
		if (m_bReturning) break;	// We are returning from a function call, so keep going
		if ((TokenTypes)m_Token == tokBreak) {
			++m_Token;
			break;
		}
		m_Token.Seek(startpos, startLineNum);
	}
	m_Token.Seek(endpos, endLineNum);
}

void CDialectInterp::DoIfThenElse()
{
	VALUE vBool;
	while (true) {
		switch ((TokenTypes)m_Token) {
			case tokIf:
			case tokElseIf:
				++m_Token;	// Eat tokIf or tokElseIf
				if (!Expression(vBool)) return;
				if (vBool.Boolean()) {
					//if (!Require(tokThen, _T("Missing 'then' in if-elif statement."))) return;
					if ((TokenTypes)m_Token == tokThen) ++m_Token;
					Interpret(tokEndIf, tokElseIf, tokElse, tokContinue, tokBreak);
					if (m_bReturning) return;
					while ((TokenTypes)m_Token == tokEOL) ++m_Token;	// Eat white spaces
					switch ((TokenTypes)m_Token) {
						case tokEndIf:
							++m_Token;
							return;
						case tokElseIf:
						case tokElse:
							SkipBlock(tokIf, tokEndIf);	// We are done, goto end of if statement
							++m_Token;	// Eat final tokEndIf
							return;
						case tokBreak:
						case tokContinue:
							return;
						default:
							SyntaxError(_T("Unexpected token in if-elif statement: '%s'"),
								(LPCTSTR)TokToString(m_Token));
							return;
					}
				}
				else {
					//if (!Require(tokThen, _T("Missing 'then' in if-elif statement."))) return;
					if ((TokenTypes)m_Token == tokThen) ++m_Token;
					// Don't perform the block since the expression was false
					SkipBlock(tokIf, tokEndIf, tokElseIf, tokElse);
				}
				break;
			case tokElse:
				++m_Token;	// Eat tokElse
				Interpret(tokEndIf, tokContinue, tokBreak);
				if (m_bReturning) return;
				if (!Require(tokEndIf, _T("Missing endif after else block"))) return;
				return;
			case tokBreak:
			case tokContinue:
				return;	// Pass the break or continue up to the loop
			case tokEOF:
				SyntaxError(_T("Reached end of file before finding an endif"));
				return;
			case tokSemicolon:
			case tokEOL:
				++m_Token;
				break;
			case tokEndIf:
				++m_Token;	// Eat tokEndIf
				return;
			case tokBackslash:
				++m_Token;
				if (!Require(tokEOL, _T("Missing EOL after line continuation token '\'")))
					return;
				break;
			default:
				SyntaxError(_T("Invalid token found in if statement block: %s"),
					(LPCTSTR)TokToString(m_Token));
				return;
		}
	}
}

void CDialectInterp::DoWhileLoop()
{
	int startLineNum = m_Token.LineNumberNoOffset();
	long startpos = m_Token.Position();
	long endpos = SkipBlock(tokWhile, tokEndWhile);
	int endLineNum = m_Token.LineNumberNoOffset();

	m_Token.Seek(startpos, startLineNum);

	VALUE vBool;
	if (!Expression(vBool)) return;

	while (vBool.Boolean()) {
		if ((TokenTypes)m_Token == tokDo) ++m_Token;
		Interpret(tokEndWhile, tokContinue, tokBreak);
		if (m_bReturning) break;	// Returning from a function call, let's keep going
		if ((TokenTypes)m_Token == tokBreak) {
			++m_Token;
			break;
		}
		m_Token.Seek(startpos, startLineNum);
		if (!Expression(vBool)) return;
	}
	m_Token.Seek(endpos, endLineNum);	// Drop back to just past the endwhile
}

void CDialectInterp::DoWith()
{
	VALUE* pPrevWith = m_DataStack.WithValuePtr();

	++m_Token;	// Eat tokWith
	int line = m_Token.LineNumber();

	VALUE vNewWith;
	if (!Expression(vNewWith)) return;
	switch (vNewWith.Type()) {
		case stFrame:
		case stClass:
		case stClassInstance:
			break;
		default:
			SyntaxError(_T("Invalid argument type in 'with' statement"));
			return;
	}

	m_DataStack.SetWithValuePtr(&vNewWith);

	Interpret(tokEndWith, tokContinue, tokBreak);
	m_DataStack.SetWithValuePtr(pPrevWith);

	if (m_bReturning == RETURN_NOT) {
		// Only continue parsing with statement if everything is normal
		switch ((TokenTypes)m_Token) {
			case tokEndWith:
				++m_Token;
				break;
			case tokBreak:
			case tokContinue:
				// Pass token up a level
				break;
			default:
				SyntaxError(_T("Missing matching 'endwith' for 'with' on line %d"), line);
				return;
		}
	}
}

void CDialectInterp::DoClass()
{
	//===> Parse a class declaration
	++m_Token;	// Eat tokClass
	if ((TokenTypes)m_Token != tokIdent) {
		SyntaxError(_T("Require the class identifier in class declaration."));
		return;
	}
	TString className = (LPCTSTR)m_Token;
	++m_Token;	// Eat class name

	VALUE vBase;
	if ((TokenTypes)m_Token == tokOpenParen) {
		++m_Token;	// Eat tokOpenParen
		if (!Expression(vBase)) return;
		if (vBase.Type() != stClass) {
			RuntimeError(_T("Specified base class value is not a class type as required"));
			return;
		}
		if (!Require(tokCloseParen, _T("Missing closing paren in class declaration"))) return;
	}

	VALUE* pVal = FindValuePtr((LPCTSTR)className, true);
	*pVal = AllocClass(className, vBase);
	//pVal->Class()->m_MemberSymTab.Set(_T("__base__"), vBase);	// To access in Dialect

	// Push table for DoFuncDef() and DoAssign()
	int StackLevel = m_DataStack.Level();
	m_DataStack.Push(m_DataStack.ModuleSymTab(), pVal->Class()->m_MemberSymTab.Frame(), m_DataStack.CodeStream());
	while ((TokenTypes)m_Token != tokEndClass) {
		switch ((TokenTypes)m_Token) {
			case tokFunc:	DoFuncDef();		break;
			case tokIdent:	DoAssignment();		break;
			case tokSemicolon:
			case tokEOL:	++m_Token;			break;
			case tokEOF:	SyntaxError(_T("Reached end of file before finding matching 'endclass'")); return;
			default:		SyntaxError(_T("Unexpected token in class declaration: "),
								(LPCTSTR)TokToString(m_Token)); return;
		}
		if (m_bReturning == RETURN_EXCEPTION) {
			m_DataStack.PopToLevel(StackLevel);
			return;
		}
	}
	++m_Token;	// Eat tokEndClass
	m_DataStack.PopToLevel(StackLevel);

	// If no __init__ function was created, set __init__ to nil so then
	// when the user constructs a class it doesn't find a base class's __init__
	//if (pVal->Class()->m_MemberSymTab.Get(_T("__init__")).Type() == stUndefined) {
	//	pVal->Class()->m_MemberSymTab.Set(_T("__init__"), AllocNil());
	//}
}

void CDialectInterp::DoFuncDef()
{
	++m_Token;	// Eat tokFunc
	if ((TokenTypes)m_Token != tokIdent) {
		SyntaxError(_T("Expected the function name identifier, found: %s"),
			(LPCTSTR)TokToString(m_Token));
		return;
	}
	VALUE* pVal = FindValuePtr((LPCTSTR)m_Token, true);
	++m_Token;	// Eat function name

	ParseFunctionBody(*pVal);
}

void CDialectInterp::ParseFunctionBody(VALUE& func)
{
	// We parse everything starting with the open paren of
	// parameters declaration, so this code is used by DoFuncDef()
	// and the expression parser when it sees tokFunc (lambda style)
	if (!Require(tokOpenParen, _T("Missing open paren in function declaration."))) return;

	// ParseParamters here
	int parmCount = 0;
	TString parms[20];
	while ((TokenTypes)m_Token != tokCloseParen) {
		switch ((TokenTypes)m_Token) {
			case tokComma:
				++m_Token;
				break;
			case tokIdent:
				parms[parmCount++] = (LPCTSTR)m_Token;
				++m_Token;
				break;
			default:
				SyntaxError(_T("Expecting a comma or paramater name in function declaration."));
				return;
		}
	}
	++m_Token;	// Eat tokCloseParen
	//=========================================================
	if ((TokenTypes)m_Token == tokColon) ++m_Token;

	// Store the code for the function in a new stream for later execution
	CByteCode* snippet = new CByteCode(m_Token, tokFunc, tokEndFunc);
	func = AllocFunction(snippet, parms, parmCount);
	func.Function()->m_EndLine = CurrentLine();
	if (!Require(tokEndFunc, _T("Missing 'endfunc' in function definition statement."))) return;
}

void CDialectInterp::DoArrayAssignment()
{
	int count = 0;
	VALUE* pVals[20];	// This is NOT the way to do this!!!
	if (!Require(tokOpenBracket, _T("Missing open bracket for list."))) return;
	while ((TokenTypes)m_Token != tokCloseBracket) {
		if ((TokenTypes)m_Token != tokIdent) {
			SyntaxError(_T("Only identifiers are allowed on left hand side of array assignment."));
			return;
		}
		pVals[count++] = FindValuePtr((LPCTSTR)m_Token, true);
		++m_Token;	// Eat symbol name
		if ((TokenTypes)m_Token == tokComma) ++m_Token;
	}
	if (!Require(tokCloseBracket, _T("Missing close bracket for list."))) return;
	if (!Require(tokAssignment, _T("Missing '=' in array assignment."))) return;

	VALUE result;
	if (!Expression(result)) return;

	if (result.Type() == stNil) {
		while (count-- > 0) {
			*(pVals[count]) = AllocNil();
		}
	}
	else if (result.Type() != stArray) {
		RuntimeError(_T("Right hand side of array assignment must reduce to an array."));
		return;
	}
	else if (result.Array()->m_Length != count) {
		RuntimeError(_T("Right hand side of array assignment contains different number of elements."));
		return;
	}
	else {
		while (count-- > 0) {
			if (pVals[count]->Type() != stProperty) {
				*(pVals[count]) = result.Array()->__get__(count + 1);
			}
			else {
				VALUE vWriteFunc = pVals[count]->Property()->m_WriteFunc;
				if (vWriteFunc.Type() == stFunction) {
					// We must store these two values because if this was
					// a property of a class and the RHS also invoked this
					// property, but from a different class instance, when
					// we go to execute the write function, we will end up
					// writing into the self from the RHS!
					CClassInstImpl* pClassInstImpl = vWriteFunc.Function()->m_pClassInstImpl;
					CClassImpl* pClassImpl = vWriteFunc.Function()->m_pClassImpl;

					VALUE vArgs[2];
					vArgs[0] = pVals[count]->Property()->m_vPropValue;
					vArgs[1] = result.Array()->__get__(count + 1);

					// Restore original values before executing write func
					vWriteFunc.Function()->m_pClassInstImpl = pClassInstImpl;
					vWriteFunc.Function()->m_pClassImpl = pClassImpl;

					VALUE vTemp;
					if (!ExecFunc(vTemp, vWriteFunc.Function(), 2, vArgs, NULL)) return;
					if (vTemp.Type() != stUndefined) pVals[count]->Property()->m_vPropValue = vTemp;
				}
				else {
					pVals[count]->Property()->m_vPropValue = result.Array()->__get__(count + 1);
				}
			}
		}
	}
}

//==============================================================================

int CDialectInterp::Precedence(TokenTypes token)
{
	// This is incomplete and currently only used in SkipExpression()
	// to decide if it needs to break
	switch (token) {
		case tokUndefined:	// Use this to skip entire expression
		case tokSemicolon:	// Soon to be way of putting two lines on one line
			return 0;
		case tokColon:		// Stops after the ':' in a ?: operation
			return 1;
		case tokQuestion:
			return 2;
		case tokAssignment:		// =
		case tokBooleanEquals:	// ==
		case tokNotEquals:
			return 3;
		case tokLess:
		case tokLessEqual:
		case tokGreater:
		case tokGreaterEqual:
			return 4;
		case tokPlus:
		case tokMinus:
		case tokOr:
			return 5;
		case tokTimes:
		case tokDivide:
		case tokTilde:
		case tokIsa:
		case tokAnd:
			return 6;
		default:
			// Since tokAnd is currently the lowest operator that uses
			// this table, the rest can be any number greater then tokAnd
			return 1000;
	}
}

void CDialectInterp::SkipExpression(TokenTypes stopIfLessThan)
{
	int openParen = 0, closeParen = 0;
	int openBrack = 0, closeBrack = 0;
	int openBrace = 0, closeBrace = 0;

	int iStopIfLessThan = Precedence(stopIfLessThan);

	while (true) {
		switch((TokenTypes)m_Token) {
			case tokEOF:
#ifdef _DEBUG
				// Although EOF is legal at the end of an expression,
				// odds are against it so let's display a warning
				// when in debug mode
				ERROR_MSG(_T("Warning"), _T("Reached EOF when skipping expression"));
#endif
			case tokSemicolon:
			case tokEOL:
			case tokTo:
			case tokThen:
			//case tokFrom:
				if ((openBrack != closeBrack) && (openParen != closeParen) && (openBrace != closeBrace)) {
					SyntaxError(_T("Mismatch in brace, bracket, or paren count during question"));
					return;
				}
				return;
			case tokBackslash:
				++m_Token;
				if (!Require(tokEOL, _T("Missing EOL after line continuation token '\'")))
					return;
				break;
			case tokColon:
			case tokComma:
				if ((openBrack == closeBrack) && (openParen == closeParen) && (openBrace == closeBrace)) {
					return;
				}
				break;
			case tokOpenParen:
				++openParen;
				break;
			case tokOpenBracket:
				++openBrack;
				break;
			case tokOpenBrace:
				++openBrace;
				break;
			case tokCloseParen:
				if (++closeParen > openParen) return;
				break;
			case tokCloseBracket:
				if (++closeBrack > openBrack) return;
				break;
			case tokCloseBrace:
				if (++closeBrace > openBrace) return;
				break;
			default:
				// Useful in boolean expression short circuiting, etc.
				if ((openBrack == closeBrack) && (openParen == closeParen) && (openBrace == closeBrace)) {
					//if ((TokenTypes)m_Token == stopToken) return;
					if (Precedence((TokenTypes)m_Token) < iStopIfLessThan) return;
				}
		}
		++m_Token;
	}
}

bool CDialectInterp::Expression(VALUE& result)
{
	bool bOK = false;	// A C++ exc would bypass setting this to false below
	_TRY {
		bOK = _Expression(&result);
	}
	_CATCH_EXCEPTION {
		DWORD code = GET_EXC_CODE;
		LPCTSTR msg = GET_EXC_MESSAGE;
		// If an C++ exception is caught, convert it into a Dialect exception...
		switch (code) {
			case EXCEPTION_SYNTAX:
				SetException(_T("esyntax"), msg, CurrentModuleName(), CurrentLine(), AllocNil());
				break;
			case EXCEPTION_RUNTIME:
				SetException(_T("eruntime"), msg, CurrentModuleName(), CurrentLine(), AllocNil());
				break;
			case EXCEPTION_INTERPRETER:
				SetException(_T("einternal"), msg, CurrentModuleName(), CurrentLine(), AllocNil());
				break;
			//case EXCEPTION_STACK_OVERFLOW:
			//	MessageBox(NULL, _T("Win32 stack overflow!"), _T("Critical Exception"), MB_OK);
			//	SetException(_T("einternal"), _T("Win32 stack overflow"), CurrentModuleName(), CurrentLine(), AllocNil());
			//	break;
			default:
				SetException(_T("einternal"), _T("+Unkown internal error"), CurrentModuleName(), CurrentLine(), AllocNil());
		}
	}

	if (!bOK && (m_bReturning != RETURN_ABORT) && (m_vException.Type() != stClassInstance)) {
		// The exception hasn't been specified yet!  Use generic...
		RuntimeError(_T("Failed to parse expression."));
	}
	return bOK;
}

bool CDialectInterp::_Expression(VALUE *result) {
	if (!Question(result)) return false;
	if (m_vException.Type() == stClassInstance) return false;
	switch((TokenTypes)m_Token) {
		// Valid ways for an expression to end
		case tokEOL:
		case tokEOF:
		case tokCloseParen:
		case tokCloseBracket:
		case tokCloseBrace:
		case tokComma:
		case tokColon:
		case tokSemicolon:
		case tokTo:
		case tokThen:
		case tokRange:
		case tokEndChunk:
		//case tokFrom:
		case tokDo:
			// Don't eat the token, but return true
			return true;
		default:
			SyntaxError(_T("Invalid token ending an expression: '%s'"),
				(LPCTSTR)TokToString(m_Token));
			return false;
	}
}

bool CDialectInterp::Question(VALUE *result)
{
	if (!FirstRelational(result)) return false;
	while ((TokenTypes)m_Token == tokQuestion) {
		++m_Token;
		if (result->Boolean()) {
			if (!_Expression(result)) return false;
			if (!Require(tokColon, _T("Missing ':' in question operator (eg. x ? y : z)"))) return false;
			SkipExpression(tokUndefined);
		}
		else {
			SkipExpression(tokColon);
			if (!Require(tokColon, _T("Missing ':' in question operator (eg. x ? y : z)"))) return false;
			if (!_Expression(result)) return false;
		}
	}
	return true;
}

bool CDialectInterp::FirstRelational(VALUE *result)
{
	VALUE temp;
	if (!SecondRelational(result)) return false;
	while (true) {
		switch ((TokenTypes)m_Token) {
			case tokAssignment:
				// = is case insensitive when comparing strings
				++m_Token;
				if (!SecondRelational(&temp)) return false;
				if ((result->Type() == stString) && (temp.Type() == stString)) {
					*result = (bool)(result->String().CompareNoCase(temp.String()) == 0);
				}
				else *result = *result == temp;
				break;
			case tokBooleanEquals:
				// == is case sensitive when comparing strings
				++m_Token;
				if (!SecondRelational(&temp)) return false;
				*result = *result == temp;
				break;
			case tokNotEqualsCase:
				// >< is case sensitive when comparing strings
				++m_Token;
				if (!SecondRelational(&temp)) return false;
				*result = *result != temp;
				break;
			case tokNotEquals:
				// <> is case insensitive when comparing strings
				++m_Token;
				if (!SecondRelational(&temp)) return false;
				if ((result->Type() == stString) && (temp.Type() == stString)) {
					*result = (bool)(result->String().CompareNoCase(temp.String()) != 0);
				}
				else *result = *result != temp;
				break;
			case tokBackslash:
				++m_Token;
				if (!Require(tokEOL, _T("Missing EOL after line continuation token '\'")))
					return false;
				break;
			default  : // End of Expression
				return true;
		}
	}
}

bool CDialectInterp::SecondRelational(VALUE *result)
{
	VALUE temp;
	if (!FirstBinary(result)) return false;
	while (true) {
		switch ((TokenTypes)m_Token) {
			case tokLess:
				++m_Token;
				if (!FirstBinary(&temp)) return false;
				*result = *result < temp;
				break;
			case tokLessEqual:
				++m_Token;
				if (!FirstBinary(&temp)) return false;
				*result = *result <= temp;
				break;
			case tokGreater:
				++m_Token;
				if (!FirstBinary(&temp)) return false;
				*result = *result > temp;
				break;
			case tokGreaterEqual:
				++m_Token;
				if (!FirstBinary(&temp)) return false;
				*result = *result >= temp;
				break;
			case tokBackslash:
				++m_Token;
				if (!Require(tokEOL, _T("Missing EOL after line continuation token '\'")))
					return false;
				break;
			default  : // End of Expression
				return true;
		}
	}
}

bool CDialectInterp::FirstBinary(VALUE *result)
{
	VALUE temp;
	if (!SecondBinary(result)) return false;
	while (true) {
		switch ((TokenTypes)m_Token) {
			case tokPlus:
				++m_Token;
				if (!SecondBinary(&temp)) return false;
				*result = *result + temp;
				break;
			case tokMinus:
				++m_Token;
				if (!SecondBinary(&temp)) return false;
				*result = *result - temp;
				break;
			case tokOr:
				++m_Token;
				if (result->Boolean() == true) {
					// Boolean short circuiting
					SkipExpression(tokOr);
					*result = true;
				}
				else {
					if (!SecondBinary(&temp)) return false;
					*result = *result || temp;
				}
				break;
			case tokBackslash:
				++m_Token;
				if (!Require(tokEOL, _T("Missing EOL after line continuation token '\'")))
					return false;
				break;
			default  : // End of Expression
				return true;
		}
	}
}

bool CDialectInterp::SecondBinary(VALUE *result) {
	VALUE temp;
	if (!Unary(result)) return false;
	while (true) {
		switch ((TokenTypes)m_Token) {
			case tokIsa:
				++m_Token;	// Eat tokIsa
				if ((TokenTypes)m_Token == tokQuotedString) {
					*result = result->Isa((LPCTSTR)m_Token);
				}
				else {
					if ((TokenTypes)m_Token == tokTick) ++m_Token;	// Symbol
					// This isn't quite right, becuase something like the following
					// isn't an error because the plus sign is seen as "+":
					//		myVar isa +
					*result = result->Isa(TokToString(m_Token));
				}
				++m_Token;
				break;
			case tokTimes:
				++m_Token;
				if (!Unary(&temp)) return false;
				*result = (*result) * temp;
				break;
			case tokDivide:
				++m_Token;
				if (!Unary(&temp)) return false;
				*result = *result / temp;
				break;
			case tokPercent:
				++m_Token;
				if (!Unary(&temp)) return false;
				if ((result->Type() != stInteger) || (temp.Type() != stInteger)) {
					RuntimeError(_T("Modulus requires two integer values"));
					return false;
				}
				else if (temp.Integer() == 0) {
					RuntimeError(_T("The second operand to modulus may not be zero"));
					return false;
				}
				*result = result->Integer() % temp.Integer();
				break;
			case tokTilde:
				++m_Token;
				if (!Unary(&temp)) return false;
				if (!Concat(*result, temp, result)) return false;
				break;
			case tokAnd:
				++m_Token;
				if (result->Boolean() == false) {
					// Boolean short circuiting, stop if we find and 'or'
					SkipExpression(tokAnd);
					*result = false;
				}
				else {
					if (!Unary(&temp)) return false;
					*result = *result && temp;
				}
				break;
			case tokBackslash:
				++m_Token;
				if (!Require(tokEOL, _T("Missing EOL after line continuation token '\'")))
					return false;
				break;
			default  : // End of Expression
				return true;
		}
	}
}

bool CDialectInterp::Unary(VALUE *result) {
	while (true) {
		switch ((TokenTypes)m_Token) {
			case tokMinus : // Unary Minus
				++m_Token;
				if (!Unary(result)) return false;
				*result = -(*result);
				return true;
			case tokNot :	// Boolean Not
				++m_Token;
				if (!Unary(result)) return false;
				*result = !(*result);
				return true;
			case tokPlus:	// Unary Plus (basically a NOP)
				++m_Token;
				if (!Unary(result)) return false;
				return true;
			case tokBackslash:
				++m_Token;
				if (!Require(tokEOL, _T("Missing EOL after line continuation token '\'")))
					return false;
				break;
			default:		// End of Expression
				return DotExpr(result);
		}
	}
}

bool CDialectInterp::DotExpr(VALUE *result) {
	TString tokenString;
	VALUE tempResult, *pvThis;
	if (!ReadNumber(result, &pvThis)) return false;
	while (true) {
		switch ((TokenTypes)m_Token) {
			case tokDot:
				++m_Token;
				if ((TokenTypes)m_Token == tokIdent) {
					tokenString = (LPCTSTR)m_Token;
				}
				else {
					tokenString = TokenTypeToKeywordString((TokenTypes)m_Token);
					if (tokenString.IsEmpty()) {
						SyntaxError(_T("Invalid dot expression indexer: ") + TokToString(m_Token));
						return false;
					}
				}
				++m_Token;

				////////////////////////////////////////////////////////
				// This may be dead code???????
				if (result->Type() == stProperty) {
					VALUE vReadFunc = result->Property()->m_ReadFunc;
					if (vReadFunc.Type() == stFunction) {
						if (!ExecFunc(tempResult, vReadFunc.Function(), 1, &result->Property()->m_vPropValue, pvThis)) return false;
						*result = tempResult;
					}
					else {
						*result = result->Property()->m_vPropValue;
					}
				}
				////////////////////////////////////////////////////////
				//else {
					*result = result->Get(tokenString);
					if (result->Type() == stProperty) {
						VALUE vReadFunc = result->Property()->m_ReadFunc;
						if (vReadFunc.Type() == stFunction) {
							if (!ExecFunc(tempResult, vReadFunc.Function(), 1, &result->Property()->m_vPropValue, pvThis)) return false;
							*result = tempResult;
						}
						else {
							*result = result->Property()->m_vPropValue;
						}
					}
				//}

				if (result->Type() == stUndefined) {
					*result = AllocNil();
				}
				break;
			case tokOpenBracket:
				++m_Token;
				if (result->Type() == stProperty) {
					VALUE vReadFunc = result->Property()->m_ReadFunc;
					if (vReadFunc.Type() == stFunction) {
						if (!ExecFunc(tempResult, vReadFunc.Function(), 1, &result->Property()->m_vPropValue, pvThis)) return false;
						*result = tempResult;
					}
					else {
						*result = result->Property()->m_vPropValue;
					}
				}
				if (!_Expression(&tempResult)) return false;
				if ((TokenTypes)m_Token == tokRange) {
					++m_Token;
					VALUE vRangeEnd;
					if (!_Expression(&vRangeEnd)) return false;
					VALUE vRangeStart = tempResult;
					tempResult = AllocArray(2);
					tempResult.Set(1, vRangeStart);
					tempResult.Set(2, vRangeEnd);
				}
				if (!Require(tokCloseBracket, _T("Missing close bracket for indexer"))) return false;
				*result = result->Get(tempResult);
				if (result->Type() == stUndefined) {
					*result = AllocNil();
				}
				break;
			case tokOpenParen:
				if (result->Type() == stFunction) {
					if (!ExecFunc(tempResult, result->Function(), -1, NULL, pvThis)) return false;
					*result = tempResult;
				}
			#if defined(DIALECT_PRO) || defined(DIALECT_ENT)
				else if ((result->Type() == stVoid) && result->Isa("dllfunc")) {
					if (!ExecDLLFunc(tempResult, result->Void(), -1, NULL)) return false;
					*result = tempResult;
				}
			#endif
				else if (result->Type() == stClass) {
					// This is a call to construct a class instance, so let's do it...
					// Indirection through tempResult is intentional, originally I just used the following line:
					//		*result = AllocClassInstance(*result, this);
					// Which caused a strange error when used with Dialect code like:
					//		message(regex("[0-9]+").match("asdf123kj"))
					// That is, where a contructed object (regex in this case) was used
					// without binding to a variable.
					// The CRegExpression object created in __init__ was bound
					// to self as it should have been, but when executing the match()
					// the self reverted to a previous state.  Binding it to a variable
					// first caused no problems, like:
					//		r = regex("[0-9]+")
					//		message(r.match("asdf123kj"))
					tempResult = AllocClassInstance(*result, this);
					{
						VALUE* pVal = tempResult.FindValuePtr(_T("__init__"), false);
						if (pVal != NULL) {
							if (pVal->Type() == stFunction) {
								VALUE ignore;
								if (!ExecFunc(ignore, pVal->Function(), -1, NULL,
									&tempResult/*.ClassInstance()->m_SelfSymTab*/))
								{
									return false;
								}
							}
							else if (pVal->Type() == stNil) {
								// No function to execute, but the parens should still
								// be there for consistency
								if (!Require(tokOpenParen, _T("Missing open paren in new statement"))) return false;
								if (!Require(tokCloseParen, _T("Missing close paren in new statement"))) return false;
							}
							else {
								SyntaxError(_T("The __init__ member of a class is not a function."));
								return false;
							}
						}
						else {
							// No function to execute, but the parens should still
							// be there for consistency
							if (!Require(tokOpenParen, _T("Missing open paren in new statement"))) return false;
							if (!Require(tokCloseParen, _T("Missing close paren in new statement"))) return false;
						}
					}
					*result = tempResult;
				}
				else {
					++m_Token;
					RuntimeError(_T("Invalid function call, not a function"));
					return false;
				}
				break;
			case tokBackslash:
				++m_Token;
				if (!Require(tokEOL, _T("Missing EOL after line continuation token '\'")))
					return false;
				break;
			default  : // End of Expression
				return true;
		}
	}
}

bool CDialectInterp::ReadNumber(VALUE *result, VALUE** pvThis)
{
	/*
	 *	This isn't necesarily a number, could be a string, etc.
	 */
	VALUE val, vTemp;

	*pvThis = NULL;	// Until otherwise discovered (with tokInherited)

	while (true) {
		switch ((TokenTypes)m_Token) {
			case tokIdent:
				{
					int bFormStr = false;
					TString name = (LPCTSTR)m_Token;
					++m_Token;	// Eat tokIdent

					VALUE* pVal = FindValuePtr(name, false);
					if (pVal == EXTERNAL_SYMBOL) {
						*result = RHSExternal(name);
						if (m_vException.Type() == stClassInstance) return false;
					}
					else if (pVal == NULL) {
						RuntimeError(_T("Unbound symbol in expression: %s"), (LPCTSTR)name);
						return false;
					}
					else {
						if (pVal->Type() == stProperty) {
							VALUE vReadFunc = pVal->Property()->m_ReadFunc;
							if (vReadFunc.Type() == stFunction) {
								if (!ExecFunc(*result, vReadFunc.Function(), 1, &pVal->Property()->m_vPropValue, NULL)) return false;
							}
							else {
								*result = pVal->Property()->m_vPropValue;
							}
						}
						else {
							*result = *pVal;
						}
					}
				}
				return true;
			case tokOpenParen :
				++m_Token;
				if (!_Expression(result)) return false;
				if (!Require(tokCloseParen, _T("Missing close paren in expression."))) return false;
				return true;
			case tokOpenBracket:
				*result = ParseListDeclaration();
				if (m_vException.Type() == stClassInstance) return false;
				return true;
			case tokOpenBrace:
				*result = ParseDictDeclaration();
				if (m_vException.Type() == stClassInstance) return false;
				return true;
			case tokInteger:
				*result = (int)m_Token;
				++m_Token;
				return true;
			case tokFloat:
				*result = (float)m_Token;
				++m_Token;
				return true;
			case tokQuotedString:
				*result = (LPCTSTR)m_Token;
				++m_Token;
				return true;
			case tokTrue:
				*result = true;
				++m_Token;
				return true;
			case tokFalse:
				*result = false;
				++m_Token;
				return true;
			case tokNil:
				++m_Token;
				*result = AllocNil();
				return true;
			case tokFunc:
				++m_Token;	// Always eat tokFunc before ParseFunctionBody()
				ParseFunctionBody(*result);
				return true;
			case tokStartChunk:
				return ParseChunkDecl(*result);
			case tokTick:
				++m_Token;
				if ((TokenTypes)m_Token == tokIdent) {
					*result = AllocSymbol((LPCTSTR)m_Token);
					++m_Token;
					return true;
				}
				else {
					// Keywords can be symbols too...
					TString symName = TokenTypeToKeywordString((TokenTypes)m_Token);
					if (!symName.IsEmpty()) {
						*result = AllocSymbol(symName);
						++m_Token;
						return true;
					}
					else {
						SyntaxError(_T("Invalid symbol name"));
						return false;
					}
				}
			case tokDot:
				// A tokDot at this level means we are within a 'with' block
				// and this expression is using the with'd item
				//		with some.var
				//			x = .member.of.somevar
				//		endwith
				// DO NOT eat the tokDot!!!
				if (m_DataStack.WithValuePtr() != NULL) {
					*result = *(m_DataStack.WithValuePtr());
					return true;
				}
				else {
					SyntaxError(_T("No preceding 'with' statement as required for this expression"));
					return false;
				}
			case tokInherited:
				{
					++m_Token;
					if (m_DataStack.ClassImpl() == NULL) {
						RuntimeError(_T("inherited can only be used in a class function"));
						return NULL;
					}
					if (!Require(tokDot, _T("Expecting a '.' after the 'inherited' token"))) return NULL;
					if ((TokenTypes)m_Token != tokIdent) {
						SyntaxError(_T("Excpecting member function identifier after inherited"));
						return false;
					}
					VALUE vInherited = m_DataStack.FunctionSymTab()->__get__(_T("inherited"));
					if (vInherited.Type() != stClass) {
						RuntimeError(_T("This class doesn't have a base from which to inherit"));
						return false;
					}
					VALUE* pv = vInherited.FindValuePtr((LPCTSTR)m_Token, false);
					if (pv == NULL) {
						RuntimeError(_T("Unable to find symbol in parent class: %s"), (LPCTSTR)m_Token);
						return false;
					}
					++m_Token;
					*result = *pv;
					if (m_DataStack.FunctionSymTab() == NULL) {
						*pvThis = NULL;
					}
					else {
						*pvThis = m_DataStack.FunctionSymTab()->FindValuePtr(_T("this"), false);
						if (result->Type() == stFunction) {
							if (*pvThis) {
								ASSERT((*pvThis)->Type() == stClassInstance);
								result->Function()->m_pClassInstImpl = (*pvThis)->ClassInstance();
							}
							else {
								result->Function()->m_pClassInstImpl = NULL;
							}
						}
						else if (result->Type() == stProperty) {
							if (result->Property()->m_ReadFunc.Type() == stFunction) {
								if (*pvThis) {
									ASSERT((*pvThis)->Type() == stClassInstance);
									result->Property()->m_ReadFunc.Function()->m_pClassInstImpl = (*pvThis)->ClassInstance();
								}
								else {
									result->Property()->m_ReadFunc.Function()->m_pClassInstImpl = NULL;
								}
							}

							if (result->Property()->m_WriteFunc.Type() == stFunction) {
								if (*pvThis) {
									ASSERT((*pvThis)->Type() == stClassInstance);
									result->Property()->m_WriteFunc.Function()->m_pClassInstImpl = (*pvThis)->ClassInstance();
								}
								else {
									result->Property()->m_WriteFunc.Function()->m_pClassInstImpl = NULL;
								}
							}

							if (result->Property()->m_vPropValue.Type() == stFunction) {
								if (*pvThis) {
									ASSERT((*pvThis)->Type() == stClassInstance);
									result->Property()->m_vPropValue.Function()->m_pClassInstImpl = (*pvThis)->ClassInstance();
								}
								else {
									result->Property()->m_vPropValue.Function()->m_pClassInstImpl = NULL;
								}
							}
						}
					}
				}
				return true;
			case tokDefined:
				++m_Token;
				if (!Require(tokOpenParen, _T("Missing open paren in call to defined()"))) return false;
				if ((TokenTypes)m_Token == tokIdent) {
					VALUE* ptr = FindValuePtr((LPCTSTR)m_Token, false);
					if (ptr == NULL) *result = false;
					else *result = (bool)(ptr->Type() != stNil);
					++m_Token;
				}
				else {
					SyntaxError(_T("defined() requires an identifier arg"));
					return false;
				}
				if (!Require(tokCloseParen, _T("Missing close paren in call to defined()"))) return false;
				return true;
			case tokStr_ :  // number to string
				++m_Token;	// Eat tokStr_
				if (!Require(tokOpenParen, _T("Missing open paren in call to str$()"))) return false;
				if (!_Expression(result)) return false;
				if (!Require(tokCloseParen, _T("Missing close paren in call to str$()"))) return false;
				*result = result->String();
				return true;
			case tokLen:	// Array length
				++m_Token;	// Eat tokLen
				if (!Require(tokOpenParen, _T("Missing open paren in call to len()"))) return false;
				if (!_Expression(result)) return false;
				if (!Require(tokCloseParen, _T("Missing close paren in call to len()"))) return false;
				*result = result->Length();
				return true;
			case tokChr_ :  // integer to ASCII char
				{
				++m_Token;	// Eat tokChr_
				if (!Require(tokOpenParen, _T("Missing open paren in call to chr$()"))) return false;
				if (!_Expression(result)) return false;
				if (!Require(tokCloseParen, _T("Missing close paren in call to chr$()"))) return false;
				TString r;
				//r.Format(_T("%c"), (TCHAR)result->Integer());
				LPTSTR pr = r.GetBuffer(1);
				pr[0] = (TCHAR)result->Integer();
				r.ReleaseBuffer(1); // Do this to allow a chr$(0) to actually be a one byte char
				*result = r;
				}
				return true;
			case tokType:
				++m_Token;
				if (!Require(tokOpenParen, _T("Missing open paren in type()"))) return false;
				if (!Expression(vTemp)) return false;
				*result = vTemp.TypeString();
				if (!Require(tokCloseParen, _T("Missing close paren in type()"))) return false;
				return true;
			case tokLeft_:
				{
				++m_Token;
				if (!Require(tokOpenParen, _T("Missing open paren in left$()"))) return false;
				if (!Expression(vTemp)) return false;
				if (!Require(tokComma, _T("Missing comma in left$()"))) return false;
				int count;
				if (!IntExpression(count)) return false;
				if (!Require(tokCloseParen, _T("Missing close paren in left$()"))) return false;
				*result = vTemp.String().Left(count);
				}
				return true;
			case tokRight_:
				{
				++m_Token;
				if (!Require(tokOpenParen, _T("Missing open paren in right$()"))) return false;
				if (!Expression(vTemp)) return false;
				if (!Require(tokComma, _T("Missing comma in right$()"))) return false;
				int count;
				if (!IntExpression(count)) return false;
				if (!Require(tokCloseParen, _T("Missing close paren in right$()"))) return false;
				*result = vTemp.String().Right(count);
				}
				return true;
			case tokProp:
				{
				++m_Token;
				if (!Require(tokOpenParen, _T("Missing open paren in prop()"))) return false;
				VALUE vInitial;
				if (!Expression(vInitial)) return false;
				if (!Require(tokComma, _T("Missing comma in prop()"))) return false;
				VALUE readFunc;
				if (!Expression(readFunc)) return false;
				if (!Require(tokComma, _T("Missing comma in prop()"))) return false;
				VALUE writeFunc;
				if (!Expression(writeFunc)) return false;
				if (!Require(tokCloseParen, _T("Missing close paren in prop()"))) return false;
				*result = AllocProperty(readFunc, writeFunc, vInitial);
				}
				return true;
			case tokMap:
				{
					++m_Token;		// Eat tokMap
					if (!Require(tokOpenParen, _T("Missing open paren in map()"))) return false;
					VALUE func;
					if (!Expression(func)) return false;
					if (func.Type() != stFunction) {
						RuntimeError(_T("First arg to map() must be a function"));
						return false;
					}
					if (!Require(tokComma, _T("Missing comma in map()"))) return false;
					VALUE vData;
					if (!Expression(vData)) return false;
					if (vData.Type() == stArray) {
						if ((func.Function()->m_ParamCount != 1) &&
							(func.Function()->m_ParamCount != -1)) // -1 means core function
						{
							RuntimeError(_T("Function passed to map() must accept exactly one argument for an array"));
							return false;
						}
						if (!Require(tokCloseParen, _T("Missing close paren in map()"))) return false;
						*result = AllocArray(vData.Length());
						for (int idx = 0; idx < vData.Length(); ++idx) {
							if (!ExecFunc(result->Array()->m_Values[idx], func.Function(), 1, &vData.Array()->m_Values[idx], NULL)) return false;
						}
					}
					else if (vData.Type() == stFrame) {
						if ((func.Function()->m_ParamCount != 2) &&
							(func.Function()->m_ParamCount != -1)) // -1 means core function
						{
							RuntimeError(_T("Function passed to map() must accept exactly two arguments for a frame"));
							return false;
						}
						if (!Require(tokCloseParen, _T("Missing close paren in map()"))) return false;
						*result = AllocFrame();
						FindNextInfo fni;
						VALUE key, value, tempResult, vArgs[2];
						while (vData.FindNext(fni, key, value)) {
							vArgs[0] = key;
							vArgs[1] = value;
							if (!ExecFunc(tempResult, func.Function(), 2, vArgs, NULL)) return false;
							if (tempResult.Type() != stNil) {
								result->Set(key, tempResult);
							}
						}
					}
					else {
						RuntimeError(_T("Second arg to map() must be an array or frame"));
						return false;
					}
				}
				return true;
			case tokEvaluate:
				++m_Token;
				if (!Require(tokOpenParen, _T("Missing open paren"))) return false;
				if (!Expression(vTemp)) return false;
				if (!Require(tokCloseParen, _T("Missing close paren"))) return false;
				EvaluateText(vTemp.String(), *result, m_DataStack.ModuleSymTab());
				if (m_vException.Type() == stClassInstance) {
					return false;
				}
				return true;
			case tokBackslash:
				++m_Token;
				if (!Require(tokEOL, _T("Missing EOL after line continuation token '\'")))
					return false;
				break;
			default:
				SyntaxError(_T("Invalid token in expression: %s"), (LPCTSTR)TokToString(m_Token));
				return false;
		}
	}
}

VALUE CDialectInterp::RHSExternal(TString symbolName)
{
	while ((TokenTypes)m_Token == tokDot) {
		++m_Token;	// Eat tokDot
		symbolName += '.';
		if ((TokenTypes)m_Token == tokIdent) {
			symbolName += (LPCTSTR)m_Token;
			++m_Token;
		}
		else {
			RuntimeError(_T("Invalid identifier: %s.<something which isn't an identifier>"),
				(LPCTSTR)symbolName);
			return AllocNil();
		}
	}
	if ((TokenTypes)m_Token == tokOpenParen) {
		// We are making a function call, not an assignment
		// Parse args, then call CallExternal(symName, argCount, args);
		VALUE args[20];
		int argCount = 0;
		if (!Require(tokOpenParen, _T("Missing open paren in function call."))) return AllocNil();
		while (((TokenTypes)m_Token != tokCloseParen) && ((TokenTypes)m_Token != tokEOF)) {
			if (argCount >= 20) {
				InternalError(_T("Too many arguments in function call"));
				return AllocNil();
			}
			else {
				if (!Expression(args[argCount++])) return AllocNil();
				if ((TokenTypes)m_Token == tokComma) ++m_Token;
			}
		}
		if (!Require(tokCloseParen, _T("Missing close paren in function call."))) return AllocNil();
		return CallExternal(symbolName, argCount, args);
	}
	else {
		return GetExternal(symbolName);
	}
}

bool CDialectInterp::FindNextCatch()
{
	// Helper function to DoTry()
	// Return true if we're at a catch to examine, false if no more catch blocks left
	// Remember not to return for catches that are part of an embeded try block

	int tryDepth = 1;
	while (tryDepth > 0) {
		switch ((TokenTypes)m_Token) {
			case tokCatch:
				if (tryDepth == 1) return true;
			case tokFinally:
				if (tryDepth == 1) return false;
				break;
			case tokTry:
				++tryDepth;
				break;
			case tokEndTry:
				if (--tryDepth == 0) return false;
				break;
			case tokEOF:
				SyntaxError(_T("Reached end of file before finding a matching endtry"));
				return false;
		}
		++m_Token;
	}
	// We should never reach this
	ASSERT(false);
	return false;
}

bool CDialectInterp::Concat(VALUE a, VALUE b, VALUE* result)
{
	SymbolTypes aType = a.Type();
	SymbolTypes bType = b.Type();

	if (aType == stNil) {
		*result = b;
	}
	else if (bType == stNil) {
		*result = a;
	}
	else if (aType != bType) {
		RuntimeError(_T("Incompatible types in concat operation: ") + a.TypeString() +
			_T(" and ") + b.TypeString());
		return false;
	}
	else if (aType == stArray) {
		int aLength = a.Length();
		int bLength = b.Length();
		*result = AllocArray(aLength + bLength);
		int idx;
		for (idx = 1; idx <= aLength; ++idx) {
			result->Set(idx, a[idx]);
		}
		for (idx = 1; idx <= bLength; ++idx) {
			result->Set(idx + aLength, b[idx]);
		}
	}
	else if (aType == stFrame) {
		*result = AllocFrame();
		VALUE key, value;
		FindNextInfo fniA;
		while (a.FindNext(fniA, key, value)) {
			result->Set(key, value);
		}
		FindNextInfo fniB;
		while (b.FindNext(fniB, key, value)) {
			result->Set(key, value);
		}
	}
	else if (aType == stString) {
		*result = a.String() + b.String();
	}
	else if (aType == stChunk) {
		int lengthA = a.GetChunkByteCount();
		int lengthB = b.GetChunkByteCount();
		*result = AllocChunk(lengthA + lengthB, NULL);
		U8* data = result->GetChunkData();
		memcpy(data, a.GetChunkData(), lengthA);
		memcpy(data + lengthA, b.GetChunkData(), lengthB);
		result->Chunk()->m_EncodingType = a.Chunk()->m_EncodingType;
		result->Chunk()->m_IndexType = a.Chunk()->m_IndexType;
		// What to do with dimensions?  This only really makes sense
		// with 1D chunks...
		if ((a.Chunk()->m_DimensionCount != 1) || (b.Chunk()->m_DimensionCount != 1)) {
			RuntimeError(_T("Only one-dimensional chunks may be concatinated"));
		}
	}
	else {
		RuntimeError(_T("Can't concat this type: ") + a.TypeString());
		return false;
	}
	return true;
}

void CDialectInterp::DoTry()
{
	++m_Token;		// Eat tokTry
	Interpret(tokEndTry, tokCatch, tokFinally);

	if (m_bReturning) {
		// If not an exception, keep going up call stack
		if (m_bReturning != RETURN_EXCEPTION) return;

		// An exception was thrown, find the correct handler and execute,
		// followed by the finally block if it exists
		while (FindNextCatch()) {
			TString exception;
			ASSERT((TokenTypes)m_Token == tokCatch);
			++m_Token;
			if ((TokenTypes)m_Token == tokIdent) {
				exception = (LPCTSTR)m_Token;
			}
			else if ((TokenTypes)m_Token == tokTimes) {
				exception = _T("*");
			}
			else {
				SyntaxError(_T("Expecting exception identifier"));
				return;
			}
			++m_Token;
			if ((exception == _T("*")) || m_vException.ClassInstance()->IsA(exception)) {
				VALUE* pvExc = NULL;
				if ((TokenTypes)m_Token == tokComma) {
					// Copy the exception into the requested variable
					++m_Token;
					if ((TokenTypes)m_Token != tokIdent) {
						SyntaxError(_T("Expecting an identifier to place exception into"));
						return;
					}
					pvExc = FindValuePtr((LPCTSTR)m_Token, true);
					*pvExc = m_vException;
					++m_Token;
				}

				// This is the handler to execute, first store values and turn
				// off the m_bReturning otherwise the following Interpret() might
				// not complete correctly
				VALUE vException = m_vException;
				m_vException = AllocNil();
				m_bReturning = RETURN_NOT;

				Interpret(tokEndTry, tokCatch, tokFinally);

				if (pvExc != NULL) *pvExc = AllocNil();	// Discard it

				if (m_bReturning) {
					// If returning, but not an exception, keep going up call stack
					// Note - this will bypass the finally block if it exists
					if (m_bReturning != RETURN_EXCEPTION) return;

					// What should we do if an exception is returned again?
					//RuntimeError(_T("Exception occured in catch block, not currently legal"));
					//return;
				}
				else {
					break;	// Done with catch blocks, execute finally if exists and leave
				}
			}
		}
	}

	// Not returning from a function and no exceptions thrown (or else already handled)
	// Execute the finally block if it exists and return
	//if ((TokenTypes)m_Token == tokCatch) {
	if (((TokenTypes)m_Token != tokFinally) && ((TokenTypes)m_Token != tokEndTry)) {
		int tryDepth = 1;
		while (tryDepth > 0) {
			++m_Token;
			switch ((TokenTypes)m_Token) {
				case tokTry:
					++tryDepth;
					break;
				case tokEndTry:
					--tryDepth;
					break;
				case tokFinally:
					if (tryDepth == 1) {
						tryDepth = 0;			// Force a break to go execute finally
					}
					break;
				case tokEOF:
					SyntaxError(_T("Reached end of file before finding a matching endtry"));
					return;
			}
		}

	}

	// Always execute the finally block if it exists
	if ((TokenTypes)m_Token == tokFinally) {
		++m_Token;

		// Turn this stuff off so the interpret succeeds
		VALUE vException = m_vException;
		int bReturning = m_bReturning;
		m_vException = AllocNil();
		m_bReturning = RETURN_NOT;

		Interpret(tokEndTry);	// Finally must end with tokEndTry

		switch (m_bReturning) {
			case RETURN_FUNCTION:
				// A return statement in a finally block halts further
				// processing of an exception (and returns immediately from the func)
				m_vException = AllocNil();
				return;
			case RETURN_DEAD:
			case RETURN_ABORT:
				return;
			case RETURN_EXCEPTION:
				SyntaxError(_T("Exception occured while executing finally block"));
				// We need to walk out of this finally block, so that if this try-finally
				// block is enclosed within another, the outer one will catch this exception
				SkipBlock(tokTry, tokEndTry);
				break;
			default:
				// Reset the data for the exception
				m_bReturning = bReturning;
				m_vException = vException;
		}
	}

	if (!Require(tokEndTry, _T("Missing endtry token"))) return;
}

void CDialectInterp::DoThrow()
{
	++m_Token;				// Eat tokThrow

	// Set normal state before going back into Expression()
	m_bReturning = RETURN_NOT;

	// !!!! Don't use EXPRESSION() macros to set m_vExpression directly !!!!
	VALUE vTemp;
	if (!Expression(vTemp)) return;	// What happens if another exception get's thrown here?
	m_vException = vTemp;

	if (m_vException.Type() != stClassInstance) {
		RuntimeError(_T("Exceptions must be class instances"));
		return;
	}

	// Overridding msg to add module name and line numbers...
	// Do I really want to do this?
	//TString msg;
	//msg.Format(_T("%s (Module: %s, Line: %d)"), (LPCTSTR)m_vException.Get(_T("msg")).String(),
	//	(LPCTSTR)CurrentModuleName(), CurrentLine());
	//m_vException.Set(_T("msg"),  msg);
	m_vException.Set(_T("callstack"), CallStack());
	m_vException.Set(_T("module"), (LPCTSTR)CurrentModuleName());
	m_vException.Set(_T("line"), CurrentLine());

	// Return to exception state and continue
	m_bReturning = RETURN_EXCEPTION;	// Walk up call stack (must be set after EXPRESSION())
}

void CDialectInterp::DoReturn()
{
	++m_Token;	// Eat tokReturn
	while (true) {
		switch ((TokenTypes)m_Token) {
			case tokSemicolon:
			case tokEOL:
			case tokEOF:
				m_bReturning = RETURN_FUNCTION;
				return;
			case tokBackslash:
				++m_Token;
				if (!Require(tokEOL, _T("Missing EOL after line continuation token '\'")))
					return;
				break;
			default:
				// Functions return there result in #result#
				VALUE vStr = _T("__result__");
				if (m_DataStack.FunctionSymTab() != NULL) {
					if (!Expression(*(m_DataStack.FunctionSymTab()->FindValuePtr(vStr, true))))
						return;
				}
				else if (m_DataStack.ModuleSymTab() != NULL) {
					if (!Expression(*(m_DataStack.ModuleSymTab()->FindValuePtr(vStr, true))))
						return;
				}
				m_bReturning = RETURN_FUNCTION;
				return;
		}
	}
}

bool CDialectInterp::ExecFuncFromCore(VALUE& result, VALUE vFunc, int argCount, VALUE args[], VALUE* self)
{
	// From inside a core function, meaning also inside interpreter execution
	// So the interp handles exceptions (i.e. no need to try and catch them)

	if (vFunc.Type() != stFunction) {
		RuntimeError(_T("Function call made in core on object that isn't a function"));
		return false;
	}

	CFunctionImpl* pFunc = vFunc.Function();
	if (self == NULL) {
		pFunc->m_pClassImpl = NULL;
		pFunc->m_pClassInstImpl = NULL;
	}
	else {
		ASSERT(self->Type() == stClassInstance);
		pFunc->m_pClassImpl = self->ClassInstance()->m_BaseClass.Class();
		pFunc->m_pClassInstImpl = self->ClassInstance();
	}
	return ExecFunc(result, pFunc, argCount, args, NULL);
}

bool CDialectInterp::ExecDLLFunc(VALUE& result, void* func, int argCount, VALUE args[])
{
#ifndef DLL_SUPPORT
	RuntimeError(_T("DLL functions are not supported in this version of Dialect"));
	return false;
#else
	ASSERT(argCount == -1);  // We want to parse our args list else we can't do by-ref

	TString symbolName;
	int chunkIndex;
	VALUE vTemp, vX;
	VALUE xArgs[16];
	VALUE* pVals[16];
	memset(pVals, 0, 16 * sizeof(VALUE*));

	CDLLFunc* pFunc = (CDLLFunc*)func;
	if (argCount == -1) {
		// We must parse the args here
		if (pFunc->m_ParamCount > 16) {
			RuntimeError(_T("Exceeded internal DLL func arg count"));
			return false;
		}
		if (!Require(tokOpenParen, _T("Missing open paren in function call."))) return false;

		for (int idx = 0; idx < pFunc->m_ParamCount; idx++) {
			if ((TokenTypes)m_Token == tokCloseParen) {
				RuntimeError(_T("Not enough args in function call."));
				return false;
			}

			if (((pFunc->m_ArgTypes[idx] == CDLLFunc::atCharp) ||
				(pFunc->m_ArgTypes[idx] == CDLLFunc::atStructp) ||
				(pFunc->m_ArgTypes[idx] == CDLLFunc::atWCharp) ||
				(pFunc->m_ArgTypes[idx] == CDLLFunc::atIntp)) &&
				(TokenTypes)m_Token == tokIdent) {
				// Get a pointer to the VALUE in case the DLL arg is for buffer
				pVals[idx] = LHSExpression(&symbolName, chunkIndex);
				if (pVals[idx] == NULL) {
					RuntimeError(_T("Unable to pass variable by reference to DLL function"));
					return false;
				}
				vTemp = *(pVals[idx]);
				if (vTemp.Type() == stProperty) {
					VALUE readFunc = vTemp.Property()->m_ReadFunc;
					if (readFunc.Type() != stFunction) {
						vTemp = vTemp.Property()->m_vPropValue;
					}
					else {
						// Must execute the read function...
						if (!ExecFunc(vX, readFunc.Function(), 1, &vTemp.Property()->m_vPropValue, NULL)) return NULL;
						vTemp = vX;
					}
				}
			}
			else {
				if (!Expression(vTemp)) return false;
			}
			xArgs[idx] = vTemp;

			if (idx < pFunc->m_ParamCount - 1) {
				if (!Require(tokComma, _T("Missing argument seperating comma in function call."))) {
					return false;
				}
			}
		}
		if (!Require(tokCloseParen, _T("Missing close paren in function call (possibly too many args)."))) {
			return false;
		}
	}

	bool bResult = true;
	_TRY {
		if (argCount == -1) {
			bResult = pFunc->Exec(result, pFunc->m_ParamCount, xArgs);
			for (int idx = 0; idx < pFunc->m_ParamCount; idx++) {
				if (pVals[idx] != NULL) {
					// Write results of DLL func back into variable in case the DLL changed anything
					if (pVals[idx]->Type() != stProperty) {
						*(pVals[idx]) = xArgs[idx];
					}
					else {
						VALUE vWriteFunc = pVals[idx]->Property()->m_WriteFunc;
						if (vWriteFunc.Type() == stFunction) {
							// We must store these two values because if this was
							// a property of a class and the RHS also invoked this
							// property, but from a different class instance, when
							// we go to execute the write function, we will end up
							// writing into the self from the RHS!
							CClassInstImpl* pClassInstImpl = vWriteFunc.Function()->m_pClassInstImpl;
							CClassImpl* pClassImpl = vWriteFunc.Function()->m_pClassImpl;

							VALUE vArgs[2];
							vArgs[0] = pVals[idx]->Property()->m_vPropValue;
							vArgs[1] = xArgs[idx];

							// Restore original values before executing write func
							vWriteFunc.Function()->m_pClassInstImpl = pClassInstImpl;
							vWriteFunc.Function()->m_pClassImpl = pClassImpl;

							if (!ExecFunc(vTemp, vWriteFunc.Function(), 2, vArgs, NULL)) {
								RuntimeError(_T("Property write function failed"));
								return false;
							}
							if (vTemp.Type() != stUndefined) pVals[idx]->Property()->m_vPropValue = vTemp;
						}
						else {
							pVals[idx]->Property()->m_vPropValue = xArgs[idx];
						}

					}
				}
			}
		}
		else {
			bResult = pFunc->Exec(result, argCount, args);
		}
	}
	_CATCH_ALL {
		bResult = false;
	}
	if (!bResult) RuntimeError(_T("Execution of DLL function failed"));
	return bResult;
#endif // DLL_SUPPORT
}

bool CDialectInterp::ExecFunc(VALUE& result, CFunctionImpl* func, int _argCount, VALUE _args[], VALUE* vThis)
{
	ASSERT(func != NULL);
	ASSERT((vThis == NULL) || (vThis->Type() == stClassInstance));

	int OldPos = -1, OldLine, StackLevel = m_DataStack.Level();
	TString funcModule, strimpl;
	CFrameImpl* pModuleFrame = NULL;

	if (func->m_ParamCount == -1) {
		// This is a core function
		if (_argCount == -1) {
			// We need to parse function args...
			VALUE args[20];
			int argCount = 0;
			if (!Require(tokOpenParen, _T("Missing open paren in function call."))) return false;
			while (((TokenTypes)m_Token != tokCloseParen) && ((TokenTypes)m_Token != tokEOF)) {
				ASSERT(argCount < 20);
				if (!Expression(args[argCount++])) return false;
				if ((TokenTypes)m_Token == tokComma) ++m_Token;
			}
			if (!Require(tokCloseParen, _T("Missing close paren in function call."))) return false;

			// Only need to push pModuleFrame in case core function calls CurrentModuleFrame()
			VALUE vMod = m_SymbolTables.Get(func->m_ModuleName);
			if (vMod.Type() != stFrame)  {
				vMod = VALUE();	// Push NULL
			}
			m_DataStack.Push(vMod.Frame(), NULL, NULL, NULL);
			bool bResult = func->ExecuteCoreFunc(result, argCount, args, this);
			m_DataStack.PopToLevel(StackLevel);
			return bResult;
		}
		else {
			// args already parsed...
			// Only need to push pModuleFrame in case core function calls CurrentModuleFrame()
			VALUE vMod = m_SymbolTables.Get(func->m_ModuleName);
			if (vMod.Type() != stFrame)  {
				vMod = VALUE();	// Push NULL
			}
			m_DataStack.Push(vMod.Frame(), NULL, NULL, NULL);
			bool bResult = func->ExecuteCoreFunc(result, _argCount, _args, this);
			m_DataStack.PopToLevel(StackLevel);
			return bResult;
		}
	}

	// Figure out what the current module is (on stack), if it's not
	// the same as this funcs code streams module, then place the correct one
	// (remember to pop it at the end)
	funcModule = func->m_CodeStream->ModuleName();
	// An empty string for parent module indicates a function that
	// doesn't care under what context it is executed becuase it doesn't
	// reference any variables outside it's own code body and params passed to it,
	// or, if it does, it is meant to use the current contexts bindings
	if (!funcModule.IsEmpty()) {
		VALUE* pVal = m_SymbolTables.FindValuePtr(funcModule, false);
		if (pVal != NULL) pModuleFrame = pVal->Frame();
	}
	if (pModuleFrame == NULL) {
		pModuleFrame = m_DataStack.ModuleSymTab();	// Resort to current module table
	}

	//if (pModuleFrame == NULL) {
	//	OutputDebugString(_T("*** ExecFunc using NULL module symbol table ***\n"));
	//}

	// Create new symboltable, place it on the symbaletable stack
	VALUE symtab = AllocFrame(31);
	symtab.Set(_T("__result__"), AllocNil());

	if (_argCount == -1) {
		if (!Require(tokOpenParen, _T("Missing open paren in function call."))) return false;
		// Match up args with declared parms and place in new local symbol table
		VALUE vTemp;
		for (int idx = 0; idx < func->m_ParamCount; idx++) {
			if ((TokenTypes)m_Token == tokCloseParen) {
				SyntaxError(_T("Not enough args in function call."));
				return false;
			}
			if (!Expression(vTemp)) return false;
			symtab.Set(func->m_ParamList[idx], vTemp);
			if (idx < func->m_ParamCount - 1) {
				if (!Require(tokComma, _T("Missing argument seperating comma in function call."))) {
					return false;
				}
			}
		}
		if (!Require(tokCloseParen, _T("Missing close paren in function call (possibly too many args)."))) {
			return false;
		}
	}
	else {
		// Match up args with declared parms and place in new local symbol table
		if (_argCount != func->m_ParamCount) {
			SyntaxError(_T("Incorrect number of args in function call"));
			return false;
		}
		for (int idx = 0; idx < _argCount; idx++) {
			symtab.Set(func->m_ParamList[idx], _args[idx]);
		}
	}

	// If the function is a class member, then it's m_Self is a symbol table (stSymbolTable)
	// to be access within the function (eg    self.count = 0).
	// We must add it to the functions local symbol table.

	// There can only be a "this" if there is an associated class instance implementation
	if (func->m_pClassInstImpl != NULL) {
		if (vThis != NULL) {
			symtab.Set(_T("this"), *vThis);
		}
		else if (func->m_pClassInstImpl) {
			VALUE forcedClassInst;
			forcedClassInst.ForceCI(stClassInstance, func->m_pClassInstImpl);
			symtab.Set(_T("this"), forcedClassInst);
		}
	}

	// There can only be an "inherited" if there is an associated class implementation
	if ((func->m_pClassImpl != NULL) && (func->m_pClassImpl->m_BaseClass.Type() == stClass)) {
		symtab.Set(_T("inherited"), func->m_pClassImpl->m_BaseClass);
	}

	// Record current stream location
	OldPos = func->m_CodeStream->Position() - 1;
	OldLine = func->m_CodeStream->LineNumber();
	func->m_CodeStream->Seek(0, 1);	// Rewind the stream first!

	if (func->m_pClassInstImpl) {
		m_DataStack.Push(pModuleFrame, symtab.Frame(), func->m_CodeStream, func->m_pClassInstImpl);
	}
	else {
		m_DataStack.Push(pModuleFrame, symtab.Frame(), func->m_CodeStream, func->m_pClassImpl);
	}
	Interpret();
	// The stack pop occurs below, after the debugger gets a chance to break

	// Reposition the stream for correct recursion.  Do this before assinging #result# to
	// the result VALUE, since it's possible the functionImpl was stored in the result
	// and we don't want to potentially garbage collect it before the following call (only
	// because it would throw an exception)
	if (OldPos != -1) func->m_CodeStream->Seek(OldPos, OldLine);

	// Is this a normal function return?  Ie. Not an exception, script abort, etc.
	if ((m_bReturning == RETURN_FUNCTION) || (m_bReturning == RETURN_NOT)) {
		m_bReturning = RETURN_NOT;				// Make sure the flag is RETURN_NOT for next function call
		result = symtab.Get(_T("__result__"));	// Get the functions return value
	}

	// We want to allow a breakpoint to be on the endfunc token, which doesn't
	// actually exists in the func->m_CodeStream, so we'll just force the issue here.
	// We are checking m_bInBreakpoint because if something is interpreted while
	// we are in a breakpoint condition, it must be the user doing some debug poking
	// and we DON'T want to break on a debug windows code
	if (!m_bInBreakpoint && (m_BreakPointFunc != NULL) && (func->m_EndLine != -1)) {
		int bReturning = m_bReturning;
		VALUE vException = m_vException;

		m_bInBreakpoint = true;
		m_BreakPointFunc(CurrentModuleName(), func->m_EndLine, this);
		m_bInBreakpoint = false;
		if ((m_bReturning != RETURN_NOT) && (m_bReturning != RETURN_FUNCTION)) {
			// Abort was called while we were in the breakpoint
			// Stop execution without further action...
			return false;
		}

		// Replace orignal exception state which might have changed while in breakpoint
		// due to DebugEvaluate(), DebugInterpretText(), or any other public func.
		m_bReturning = bReturning;
		m_vException = vException;
	}

	m_DataStack.PopToLevel(StackLevel);	// Make sure we go back to correct stack level

	return (m_bReturning == RETURN_NOT);
}

void CDialectInterp::EvaluateText(LPCTSTR str, VALUE& result, CFrameImpl* context)
{
	// Take a string the represents the RHS of an expression and return the value
	TString moduleName;
	if (context != NULL) {
		VALUE vName = context->__get__(_T("__module__"));
		if (vName.Type() == stString) {
			moduleName = vName.String();
		}
	}
	else {
		moduleName = _T("evaluateText");
	}

	CByteCode token;
	if (!token.CompileText(str, moduleName)) {
		RuntimeError(token.GetLastError());
	}
	else {
		int StackLevel = m_DataStack.Level();
		m_DataStack.Push(context, NULL, &token);
		Expression(result);		// Make sure stack get's popped even if exception...
		m_DataStack.PopToLevel(StackLevel);
	}
}

VALUE CDialectInterp::ParseListDeclaration()
{
	/*
	 *	Can contain anything - eg, [1, "hello"]
	 */
	int count = 0;
	int size = 64;
	VALUE vTemp, vArray = AllocArray(size);

	if (!Require(tokOpenBracket, _T("Missing open bracket for list."))) return VALUE();
	bool bLoop = true;
	while (bLoop) {
		switch ((TokenTypes)m_Token) {
			case tokSemicolon:
			case tokEOL:
			case tokComma:
				++m_Token;
				break;
			case tokBackslash:
				++m_Token;
				if (!Require(tokEOL, _T("Missing EOL after line continuation token '\'")))
					return VALUE();
				break;
			case tokCloseBracket:
				bLoop = false;
				break;
			default:
				if (++count > size) {
					size += 64;
					vArray.Resize(size);	// Get more room
				}
				if (!Expression(vTemp)) return VALUE();
				vArray.Set(count, vTemp);
		}
	}
	if (!Require(tokCloseBracket, _T("Missing close bracket for list."))) return VALUE();
	if (count != size) vArray.Resize(count);
	return vArray;
}

bool CDialectInterp::ParseChunkDecl(VALUE& vResult)
{
	// x = [:{encoding:"base64", itype:"ui1"},"asdf23s=":]

	ASSERT((TokenTypes)m_Token == tokStartChunk);
	++m_Token;	// Eat [:
	VALUE vFrame, vData;
	if (Expression(vFrame)) {
		if (vFrame.Type() != stFrame) {
			RuntimeError(_T("Expecting a frame in chunk declaration"));
		}
		else if (Require(tokComma, _T("Missing comma in chunk declaration"))) {
			if (Expression(vData) && Require(tokEndChunk, _T("Missing :] in chunk declaration"))) {
				int encoding = enc_base64;
				int itype = it_ui1;
				TString sEncoding = vFrame[_T("encoding")].String();
				TString sitype = vFrame[_T("itype")].String();
				sEncoding.MakeLower();
				sitype.MakeLower();

				if (sEncoding == _T("base64")) encoding = enc_base64;
				else if (sEncoding == _T("hex")) encoding = enc_hex;
				else if (sEncoding == _T("ascii")) encoding = enc_ascii;
				//else if (sEncoding == _T("binary")) encoding = enc_binary;
				else if (!sEncoding.IsEmpty()) encoding = enc_undefined;

				if (sitype == _T("i1")) itype = it_i1;
				else if (sitype == _T("i2")) itype = it_i2;
				else if (sitype == _T("i4")) itype = it_i4;
				else if (sitype == _T("ui1")) itype = it_ui1;
				else if (sitype == _T("ui2")) itype = it_ui2;
				//else if (sitype == _T("ui3")) itype = it_ui3;
				//else if (sitype == _T("ui4")) itype = it_ui4;
				else if (sitype == _T("f4")) itype = it_f4;
				//else if (sitype == _T("f8")) itype = it_f8;
				else if (!sitype.IsEmpty()) itype = it_undefined;

				if ((encoding == enc_undefined) || (itype == it_undefined)) {
					RuntimeError(_T("Unknown or unsupported encoding or itype for chunk"));
				}
				else {
					bool bDim = false;
					int dim = 0;
					VALUE vDim = vFrame[_T("dim")];
					if (vDim.Type() == stInteger) {
						dim = vDim.Integer();
						bDim = true;
					}

					if (dim < 0) {
						RuntimeError(_T("dim is negative: %d"), dim);
					}
					else if (vData.Type() == stChunk) {
						// Just modify specifications
						vData.Chunk()->m_EncodingType = encoding;
						vData.Chunk()->m_IndexType = itype;
						vResult = vData;
						if (bDim) vResult.Resize(dim);
						return true;
					}
					else if (vData.Type() == stString) {
						// Create chunk by decoding the text
						vResult = AllocChunk(vData.String(), encoding, itype);
						if (bDim) vResult.Resize(dim);
						return true;
					}
					else if (vData.Type() == stNil) {
						// Create a zero initialized chunk
						vResult = AllocChunk(0, NULL);
						vResult.Chunk()->m_EncodingType = encoding;
						vResult.Chunk()->m_IndexType = itype;
						if (bDim) vResult.Resize(dim);
						return true;
					}
					else {
						RuntimeError(_T("Invalid type in chunk declaration: %s"),
							(LPCTSTR)vData.TypeString());
					}
				}
			}
		}
	}
	vResult = VALUE();
	return false;
}

VALUE CDialectInterp::ParseDictDeclaration()
{
	/*
	 *	eg, {"first":1, "second":"hello"}
	 *	or, {first:1, second:"hello"}
	 */
	TString key;
	VALUE vValue;
	VALUE Frame = AllocFrame(61);

	if (!Require(tokOpenBrace, _T("Missing open brace for dictionary."))) return AllocNil();
	while (true) {
		switch ((TokenTypes)m_Token) {
			case tokCloseBrace:
				++m_Token;
				return Frame;
			case tokEOF:
				RuntimeError(_T("Reached end of file before close brace in dictionary decl."));
				return AllocNil();
			case tokEOL:
			case tokComma:
				++m_Token;
				break;
			case tokBackslash:
				++m_Token;
				if (!Require(tokEOL, _T("Missing EOL after line continuation token '\'")))
					return AllocNil();
				break;
			default:
				switch ((TokenTypes)m_Token) {
					case tokIdent:
					case tokQuotedString:
						key = (LPCTSTR)m_Token;
						++m_Token;
						break;
					default:
						// Allow keywords to be used as frame keys
						key = TokenTypeToKeywordString((TokenTypes)m_Token);
						if (key.IsEmpty()) {
							SyntaxError(_T("Invalid key in dictionary declaration"));
							return AllocNil();
						}
						else {
							++m_Token;
						}
				}
				if (!Require(tokColon, _T("Missing colon separator in dictionary declaration."))) return VALUE();
				while ((TokenTypes)m_Token == tokEOL) ++m_Token;
				if (!Expression(vValue)) return AllocNil();
				Frame.Frame()->Add(key, vValue);
		}
	}
}

//========================================================================

VALUE CDialectInterp::CantWriteMe(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	pInterp->RuntimeError(_T("Sorry, your can't assign to this value"));
	return args[0];
}

VALUE CDialectInterp::WriteScriptPath(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	if (args[1].Type() != stString) {
		pInterp->SyntaxError(_T("Must set script path with string type"));
		return args[0];	// Original value
	}

	static_cast<CDialectInterp*>(pInterp)->SetScriptPath(args[1].String());
	return args[1].String();	// New value
}

void CDialectInterp::SetInitialPath(TString path)
{
	m_InitialPath = path;
	SetScriptPath(path);
}

#define ADD_SLASH(dir) 	if (!dir.IsEmpty() && (dir[dir.GetLength() - 1] != DIR_SEPARATOR)) dir += DIR_SEPARATOR;

void CDialectInterp::SetScriptPath(TString path)
{
	// We'll overwrite the previous property, could just set it's value...
	SetEnvironVar(_T("path"), AllocProperty(AllocNil(), AllocFunction(WriteScriptPath, _T("")), path));

	m_PathCount = 0;

	TString temp;
	for (int idx = 0; idx < MAX_PATH_ENTRIES; ++idx) {
		int pos = path.Find(';');
		if (pos >= 0) {
			temp = path.Left(pos);
			ADD_SLASH(temp);
			if (!temp.IsEmpty()) m_ScriptPath[m_PathCount++] = temp;
			path = path.Mid(pos + 1);
		}
		else {
			temp = path;
			ADD_SLASH(temp);
			if (!temp.IsEmpty()) m_ScriptPath[m_PathCount++] = temp;
			break;
		}
	}
}

//============================================================================

/*TString CDialectInterp::Field(TString from, TString split, int fieldNum)
{
	int pos = 0;
	while (fieldNum > 0) {
		pos = from.Find(split);
		if (fieldNum == 1) {
			if (pos == -1)
				return from;	// All that's left is the result
			else
				return from.Left(pos);
		}
		else {
			from = from.Mid(pos + split.GetLength());
			fieldNum--;
		}
	};
	return _T("");
}

bool CDialectInterp::funcField_(VALUE* result)
{
	++m_Token;	// Eat tokField_
	if (!Require(tokOpenParen, _T("Missing open paren in call to field() function."))) return false;
	VALUE from;
	if (!Expression(from)) return false;
	if (!Require(tokComma, _T("Missing comma in call to field() function."))) return false;
	VALUE split;
	if (!Expression(split)) return false;
	if (!Require(tokComma, _T("Missing comma in call to field() function."))) return false;
	int fieldNum;
	if (!IntExpression(fieldNum)) return false;
	if (!Require(tokCloseParen, _T("Missing close paren in call to field() function."))) return false;

	*result = Field(from.String(), split.String(), fieldNum);
	return true;
}*/

//============================================================================

VALUE* CDialectInterp::FindValuePtr(TString name, bool bCreateOK)
{
	// FindValuePtr only looks for the initial symbol in a dotted symbol
	// eg. It only finds the 'testmod' in 'testmod.func'
	// The rest is done by either Expression() or LHSExpression()

	if (name.CompareNoCase(_T("global")) == 0) {
		return &m_GlobalSymTab;
	}

	VALUE* result = m_SpecialSymTab.Frame()->FindValuePtr(name, false);

	if ((result == NULL) && (m_DataStack.FunctionSymTab() != NULL)) {
		// Don't create anything on the first run through the functions
		// local symtab, we just need to give priority to func arg variables, etc.
		result = m_DataStack.FunctionSymTab()->FindValuePtr(name, false);
	}

	if ((result == NULL) && (m_DataStack.ClassImpl() != NULL)) {
		// Never allow creation within the classimpl here
		// and don't look through the self sym tab in this case
		result = m_DataStack.ClassImpl()->FindValuePtrNoSelf(name);
		//result = m_DataStack.ClassImpl()->FindValuePtr(name, false);
	}

	if ((result == NULL) && (IsExternal(name))) {
		result = EXTERNAL_SYMBOL;
	}

	if ((result == NULL) && (m_DataStack.ModuleSymTab() != NULL)) {
		// Only allow creation in the modules namespace if we are not
		// executing a function, since a declaration in a function should
		// only exist within the functions scope
		bool bCreateHere = (bool)(bCreateOK && (m_DataStack.FunctionSymTab() == NULL));
		result = m_DataStack.ModuleSymTab()->FindValuePtr(name, bCreateHere);
	}

	if ((result == NULL) && (m_DataStack.FunctionSymTab() != NULL) && bCreateOK) {
		// This time through the func sym tab, allow creation of local args
		// Actually, we already know it's not here, so
		// just use a Add command (if bCreateOK == true) for speed...
		result = &m_DataStack.FunctionSymTab()->Add(name, VALUE())->m_Value;
	}

	return result;
}

bool CDialectInterp::IsExternal(TString symbolName)
{
	return false;
}

void CDialectInterp::SetExternal(TString symbolName, VALUE value)
{
	InternalError(_T("SetExternal() should not have been called internally"));
}

VALUE CDialectInterp::GetExternal(TString symbolName)
{
	InternalError(_T("SetExternal() should not have been called internally"));
	return AllocNil();
}

VALUE CDialectInterp::CallExternal(TString symbolName, int argCount, VALUE args[])
{
	RuntimeError(_T("Symbol '%s' does not exist current in scope."), (LPCTSTR)symbolName);
	return AllocNil();
}

//==============================================================================

VALUE* CDialectInterp::LHSExpression(TString* symbolName, int& chunkIndex)
{
	VALUE vTemp, *pvThis = NULL;
	VALUE* result;

	chunkIndex = -1; // Meaning not used

	if ((TokenTypes)m_Token == tokDot) {
		symbolName->Empty();
		result = m_DataStack.WithValuePtr();
		if (result == NULL) {
			SyntaxError(_T("Expected a preceeding 'with' statement"));
			return NULL;
		}
	}
	else if ((TokenTypes)m_Token == tokInherited) {
		++m_Token;
		if (m_DataStack.ClassImpl() == NULL) {
			RuntimeError(_T("inherited can only be used in a class function"));
			return NULL;
		}
		*symbolName = _T("inherited.");
		if (!Require(tokDot, _T("Expecting a '.' after the 'inherited' token"))) return NULL;
		if ((TokenTypes)m_Token != tokIdent) {
			SyntaxError(_T("Excpecting member function identifier after inherited"));
			return NULL;
		}
		*symbolName += (LPCTSTR)m_Token;
		VALUE vInherited = m_DataStack.FunctionSymTab()->__get__(_T("inherited"));
		if (vInherited.Type() != stClass) {
			RuntimeError(_T("This class doesn't have a base from which to inherit"));
			return NULL;
		}
		result = vInherited.FindValuePtr((LPCTSTR)m_Token, false);
		++m_Token;
		if (m_DataStack.FunctionSymTab() == NULL) {
			pvThis = NULL;
		}
		else {
			pvThis = m_DataStack.FunctionSymTab()->FindValuePtr(_T("this"), false);
			if (result->Type() == stFunction) {
				if (pvThis) {
					ASSERT(pvThis->Type() == stClassInstance);
					result->Function()->m_pClassInstImpl = pvThis->ClassInstance();
				}
				else {
					result->Function()->m_pClassInstImpl = NULL;
				}
			}
			else if (result->Type() == stProperty) {
				if (result->Property()->m_ReadFunc.Type() == stFunction) {
					if (pvThis) {
						ASSERT(pvThis->Type() == stClassInstance);
						result->Property()->m_ReadFunc.Function()->m_pClassInstImpl = pvThis->ClassInstance();
					}
					else {
						result->Property()->m_ReadFunc.Function()->m_pClassInstImpl = NULL;
					}
				}

				if (result->Property()->m_WriteFunc.Type() == stFunction) {
					if (pvThis) {
						ASSERT(pvThis->Type() == stClassInstance);
						result->Property()->m_WriteFunc.Function()->m_pClassInstImpl = pvThis->ClassInstance();
					}
					else {
						result->Property()->m_WriteFunc.Function()->m_pClassInstImpl = NULL;
					}
				}

				if (result->Property()->m_vPropValue.Type() == stFunction) {
					if (pvThis) {
						ASSERT(pvThis->Type() == stClassInstance);
						result->Property()->m_vPropValue.Function()->m_pClassInstImpl = pvThis->ClassInstance();
					}
					else {
						result->Property()->m_vPropValue.Function()->m_pClassInstImpl = NULL;
					}
				}
			}
		}
	}
	else {
		*symbolName = (LPCTSTR)m_Token;
		if (symbolName->CompareNoCase(_T("this")) == 0) {
			// Do not allow a "this" variable to be created!!!
			if (m_DataStack.FunctionSymTab() != NULL) {
				result= m_DataStack.FunctionSymTab()->FindValuePtr(_T("this"), false);
			}
			else result = NULL;	// Just to be sure

			if (result == NULL) {
				SyntaxError(_T("There is no 'this' object.  It only exists in class instance objects."));
				return NULL;
			}
		}
		else {
			result = FindValuePtr(*symbolName, true);
		}
		++m_Token;
	}

	TString tokenString;
	while (true) {
		switch ((TokenTypes)m_Token) {
		case tokDot:
			++m_Token;
			if ((TokenTypes)m_Token == tokIdent) {
				tokenString = (LPCTSTR)m_Token;
			}
			else {
				tokenString = TokenTypeToKeywordString((TokenTypes)m_Token);
				if (tokenString.IsEmpty()) {
					SyntaxError(_T("Invalid LHS dot expression indexer: ") + TokToString(m_Token));
					return false;
				}
			}
			++m_Token;

			if (result == NULL) {
				RuntimeError(_T("Undefined symbol on LHS, can't index: %s"), (LPCTSTR)(*symbolName));
				return NULL;
			}
			while (result->Type() == stProperty) {
				VALUE vReadFunc = result->Property()->m_ReadFunc;
				if (vReadFunc.Type() == stFunction) {
					if (!ExecFunc(vTemp, vReadFunc.Function(), 1, &result->Property()->m_vPropValue, pvThis)) return NULL;
					result = &vTemp;
				}
				else {
					result = &result->Property()->m_vPropValue;
				}
			}
			*symbolName += '.';
			*symbolName += tokenString;
			result = result->FindValuePtr(tokenString, true);
			break;
		case tokOpenBracket:
			if (result == NULL) {
				RuntimeError(_T("Undefined symbol on LHS, can't index: %s"), (LPCTSTR)(*symbolName));
				return NULL;
			}
			while (result->Type() == stProperty) {
				VALUE vReadFunc = result->Property()->m_ReadFunc;
				if (vReadFunc.Type() == stFunction) {
					if (!ExecFunc(vTemp, vReadFunc.Function(), 1, &result->Property()->m_vPropValue, pvThis)) return NULL;
					result = &vTemp;
				}
				else {
					result = &result->Property()->m_vPropValue;
				}
			}
			++m_Token;	// Eat open bracket
			if (!Expression(vTemp)) return NULL;
			if (!Require(tokCloseBracket, _T("Missing close bracket for indexer"))) return NULL;
			if (result->Type() != stChunk) {
				result = result->FindValuePtr(vTemp, true);
			}
			else {
				// This agregious hack allows one dimensional chunk indexing on LHS
				if (vTemp.Type() != stInteger) {
					RuntimeError(_T("Chunk indexer must be an integer"));
					return NULL;
				}
				else {
					chunkIndex = vTemp.Integer();
					return result;
				}
			}
			break;
		case tokOpenParen:
			if (result == NULL) {
				RuntimeError(_T("Undefined symbol on LHS, not a function: %s"), (LPCTSTR)(*symbolName));
				return NULL;
			}
			else if (result == EXTERNAL_SYMBOL) {
				++m_Token;
				int count = 0;
				VALUE args[20];
				while ((TokenTypes)m_Token != tokCloseParen) {
					if (!Expression(args[count++])) return NULL;
					if ((TokenTypes)m_Token == tokComma) ++m_Token;
				}
				++m_Token;	// Eat tokCloseParen
				vTemp = CallExternal(*symbolName, count, args);
				result = &vTemp;
			}
		#if defined(DIALECT_PRO) || defined(DIALECT_ENT)
			else if ((result->Type() == stVoid) && result->Isa("dllfunc")) {
				if (!ExecDLLFunc(vTemp, result->Void(), -1, NULL)) return NULL;
				result = &vTemp;	// Don't assign to the function value itself!
				// Under certain circumstances, it is ok to coninue evaluating LHS
			}
		#endif
			else if (result->Type() != stFunction) {
				SyntaxError(_T("Attempt to make function call on object that isn't a function"));
				return NULL;
			}
			else {
				if (!ExecFunc(vTemp, result->Function(), -1, NULL, pvThis)) return NULL;
				result = &vTemp;	// Don't assign to the function value itself!
				// Under certain circumstances, it is ok to coninue evaluating LHS
			}
			break;
		default:
			if (result == &vTemp) return NULL;
			else return result;
		}
	}
}

void CDialectInterp::DoAssignment()
{
	TString symbolName;
	VALUE vTemp;
	int chunkIndex = -1;
	VALUE* vLHS = LHSExpression(&symbolName, chunkIndex);
	if (m_vException.Type() == stClassInstance) return;

	if (vLHS == NULL) {
		if ((TokenTypes)m_Token == tokAssignment) {
			RuntimeError(_T("Can't perform assignment, left-hand side does not exist"));
		}
		return;
	}

	while (true) {
		switch ((TokenTypes)m_Token) {
			case tokAssignment:
				++m_Token;
				/*if (vLHS->Type() == stNil) {
					RuntimeError(_T("The left hand side of the expression evaluates to nil"));
				}
				else*/
				if (vLHS == EXTERNAL_SYMBOL) {
					if (!Expression(vTemp)) return;
					SetExternal(symbolName, vTemp);
				}
				else {
					if (vLHS->Type() == stProperty) {
						VALUE vWriteFunc = vLHS->Property()->m_WriteFunc;
						if (vWriteFunc.Type() == stFunction) {
							// We must store these two values because if this was
							// a property of a class and the RHS also invoked this
							// property, but from a different class instance, when
							// we go to execute the write function, we will end up
							// writing into the self from the RHS!
							CClassInstImpl* pClassInstImpl = vWriteFunc.Function()->m_pClassInstImpl;
							CClassImpl* pClassImpl = vWriteFunc.Function()->m_pClassImpl;

							VALUE vArgs[2];
							vArgs[0] = vLHS->Property()->m_vPropValue;
							if (!Expression(vArgs[1])) return;

							// Restore original values before executing write func
							vWriteFunc.Function()->m_pClassInstImpl = pClassInstImpl;
							vWriteFunc.Function()->m_pClassImpl = pClassImpl;

							if (!ExecFunc(vTemp, vWriteFunc.Function(), 2, vArgs, NULL)) return;
							if (vTemp.Type() != stUndefined) vLHS->Property()->m_vPropValue = vTemp;
						}
						else {
							if (!Expression(vLHS->Property()->m_vPropValue)) return;
						}
					}
					else {
						// DO NOT DO THIS --> Expression(*vLHS)
						if (!Expression(vTemp)) return;
						else if (chunkIndex == -1) {
							*vLHS = vTemp;
						}
						else {
							// Hack for chunks
							ASSERT(vLHS->Type() == stChunk);
							vLHS->Set(chunkIndex, vTemp);
						}
					}
				}
				return;
			case tokSemicolon:
			case tokEOL:
			case tokEOF:
				// LHSExpression possibly made a function call
				return;
			case tokBackslash:
				++m_Token;
				if (!Require(tokEOL, _T("Missing EOL after line continuation token '\'")))
					return;
				break;
			default:
				SyntaxError(_T("Expected assignment or function call.  Unexpected token: %s"),
					(LPCTSTR)TokToString(m_Token));
				return;
		}
	}
}

///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//	Input / Output related functions
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////

void CDialectInterp::TextOutFormated(LPCTSTR parmString, ...)
{
	TCHAR buf[1024];
	va_list rest;
	va_start(rest, parmString);
	_vstprintf(buf, parmString, rest);
	va_end(rest);
	TextOut(buf);
}

void CDialectInterp::TextOut(LPCTSTR text)
{
	if (m_WriteFunc != NULL) {
		VALUE vTemp = text;
		if (!ExecFunc(VALUE(), m_WriteFunc, 1, &vTemp, &m_vOutputDev)) return;
	}
}

void CDialectInterp::DoDebugOut()
{
	++m_Token;	// Eat tokDebugOut

	VALUE vTemp;
	while (true) {
		switch ((TokenTypes)m_Token) {
			case tokComma:
				++m_Token;
				break;
			case tokEOF:
			case tokEOL:
			case tokSemicolon:
				return;
			case tokBackslash:
				++m_Token;
				if (!Require(tokEOL, _T("Missing EOL after line continuation token '\'")))
					return;
				break;
			default:
				if (!Expression(vTemp)) return;
				if (m_DebugOutFunc != NULL) (*m_DebugOutFunc)(vTemp.String());
		}
	}
}

void CDialectInterp::DoPrint()
{
	++m_Token;	// Eat tokPrint
	while (true) {
		switch ((TokenTypes)m_Token) {
			case tokSemicolon:
			case tokEOL:
			case tokEOF:
				return;
			case tokBackslash:
				++m_Token;
				if (!Require(tokEOL, _T("Missing EOL after line continuation token '\'")))
					return;
				break;
			case tokCR:
				++m_Token;
				if (m_CRFunc != NULL) {
					if (!ExecFunc(VALUE(), m_CRFunc, 0, NULL, &m_vOutputDev)) return;
				}
				break;
			case tokTab:
				++m_Token;
				if (m_TabFunc != NULL) {
					if (!ExecFunc(VALUE(), m_TabFunc, 0, NULL, &m_vOutputDev)) return;
				}
				break;
			case tokComma:
				// Consecutive commas like:  print "hi",,,"there",,
				// Should be a syntax error?
				++m_Token;
				break;
			default:
				{
				VALUE vString;
				if (!Expression(vString)) return;
				TextOut(vString.String());
				}
		}
	}
}

void CDialectInterp::DoSItem()
{
	++m_Token;	// Eat tokSItem

	VALUE args[2];
	if (!Expression(args[0])) return;
	if (!Require(tokComma, _T("Missing comma in 'sectItem' statement"))) return;
	if (!Expression(args[1])) return;

	if ((m_SectItemFunc != NULL) && args[1].Boolean()) {
		if (!ExecFunc(VALUE(), m_SectItemFunc, 2, args, &m_vOutputDev)) return;
	}
}

void CDialectInterp::DoStartSect()
{
	++m_Token;	// Eat tokStartSect

	VALUE args[3];
	if (!Expression(args[0])) return;
	if ((TokenTypes)m_Token == tokComma) {
		++m_Token;
		if (!Expression(args[1])) return;
		if ((TokenTypes)m_Token == tokComma) {
			++m_Token;
			if (!Expression(args[2])) return;
			if (args[2].Type() != stInteger) {
				SyntaxError(_T("Third parameter in startSect must be an integer"));
				return;
			}
		}
		else args[2] = 1;
	}
	else {
		args[1] = _T("");
		args[2] = 1;	// 1 means bold font
	}

	if (m_StartSectFunc != NULL) {
		if (!ExecFunc(VALUE(), m_StartSectFunc, 3, args, &m_vOutputDev)) return;
	}
}

void CDialectInterp::DoEndSect()
{
	++m_Token;	// Eat tokEndSect

	if (m_EndSectFunc != NULL) {
		if (!ExecFunc(VALUE(), m_EndSectFunc, 0, NULL, &m_vOutputDev)) return;
	}
}

static void GetFunc(VALUE& vClassInst, LPCTSTR name, CFunctionImpl** func, CInterpreter* pInterp)
{
	VALUE* vTemp = vClassInst.FindValuePtr(name, false);
	if ((vTemp != NULL) && (vTemp->Type() == stFunction)) {
		*func = vTemp->Function();
	}
	else *func = NULL;
}

VALUE CDialectInterp::WriteOutputDev(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp)
{
	// The write function for the "output" property
	// args[0] is the current output class inst, args[1] is the new output class inst

	CDialectInterp *pDInterp = static_cast<CDialectInterp*>(pInterp);

	if (args[1].Type() == stNil) {
		pDInterp->m_vOutputDev = AllocNil();
		pDInterp->m_WriteFunc = NULL;
		pDInterp->m_TabFunc = NULL;
		pDInterp->m_CRFunc = NULL;
		return args[1];
	}
	else if (args[1].Type() != stClassInstance) {
		pInterp->RuntimeError(_T("Only a class instance or nil may be assigned to 'output'"));
		return args[0];	// Keep old value
	}

	GetFunc(args[1], _T("write"), &pDInterp->m_WriteFunc, pInterp);
	GetFunc(args[1], _T("writeline"), &pDInterp->m_CRFunc, pInterp);
	GetFunc(args[1], _T("tab"), &pDInterp->m_TabFunc, pInterp);
	GetFunc(args[1], _T("startsection"), &pDInterp->m_StartSectFunc, pInterp);
	GetFunc(args[1], _T("endsection"), &pDInterp->m_EndSectFunc, pInterp);
	GetFunc(args[1], _T("sectitem"), &pDInterp->m_SectItemFunc, pInterp);
	pDInterp->m_TableFunc = NULL;

	pDInterp->m_vOutputDev = args[1];
#pragma message("************ Solve me! *************")
	// Problems when some objects have a __del__
	return args[1];//AllocNil();	// Don't return args[1] !!!!!!!
}

///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//	Exception handling related functions
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////

bool CDialectInterp::Require(TokenTypes tok, LPCTSTR message)
{
	if ((TokenTypes)m_Token != tok) {
		SyntaxError(message);
		return false;
	}
	++m_Token;	// Eat the required token
	return true;
}

void CDialectInterp::SyntaxError(LPCTSTR parmString, ...)
{
	TCHAR buf[512];
	va_list rest;
	va_start(rest, parmString);
	_vstprintf(buf, parmString, rest);
	va_end(rest);

	SetException(_T("esyntax"), buf, AllocNil());
}

void CDialectInterp::RuntimeError(LPCTSTR parmString, ...)
{
	TCHAR buf[512];
	va_list rest;
	va_start(rest, parmString);
	_vstprintf(buf, parmString, rest);
	va_end(rest);

	SetException(_T("eruntime"), buf, AllocNil());
}

void CDialectInterp::InternalError(LPCTSTR parmString, ...)
{
	TCHAR buf[512];
	va_list rest;
	va_start(rest, parmString);
	_vstprintf(buf, parmString, rest);
	va_end(rest);

	SetException(_T("einternal"), buf, AllocNil());
}

void CDialectInterp::ClearException()
{
	m_bReturning = RETURN_NOT;
	m_vException = AllocNil();
}

void CDialectInterp::SetException(TString exc, TString msg, VALUE vOther)
{
	int line;
	TString module;
	if ((m_DataStack.Level() < 0) || (m_DataStack.CodeStream() == NULL)) {
		module = _T("__core__");
		line = 0;
	}
	else {
		line = CurrentLine();
		module = CurrentModuleName();
	}

	SetException(exc, msg, module, line, vOther);
}

void CDialectInterp::SetException(TString exc, TString msg, TString module, int line, VALUE vOther)
{
	static VALUE vGenericClass = AllocClass(new CEException());
	static VALUE vSyntaxClass = AllocClass(new CESyntax());
	static VALUE vRuntimeClass = AllocClass(new CERuntime());
	static VALUE vInternalClass = AllocClass(new CEInternal());
	static VALUE vSocketClass = AllocClass(new CESocket());
	static VALUE vTimeoutClass = AllocClass(new CETimeout());

	TString header;
	if (exc.CompareNoCase(_T("esyntax")) == 0) {
		header = _T("Syntax Error: ");
		m_vException = AllocClassInstance(vSyntaxClass, this);
	}
	else if (exc.CompareNoCase(_T("eruntime")) == 0) {
		header = _T("Runtime Error: ");
		m_vException = AllocClassInstance(vRuntimeClass, this);
	}
	else if (exc.CompareNoCase(_T("einternal")) == 0) {
		header = _T("Internal Error: ");
		m_vException = AllocClassInstance(vInternalClass, this);
	}
	else if (exc.CompareNoCase(_T("esocket")) == 0) {
		header = _T("Socket Error: ");
		m_vException = AllocClassInstance(vSocketClass, this);
	}
	else if (exc.CompareNoCase(_T("etimeout")) == 0) {
		header = _T("Timeout Error: ");
		m_vException = AllocClassInstance(vTimeoutClass, this);
	}
	else {
		// We didn't find a match, throw the generic exception
		m_vException = AllocClassInstance(vGenericClass, this);
	}

	m_vException.Set(_T("msg"), header + msg);
	m_vException.Set(_T("module"), module);
	m_vException.Set(_T("line"), line);
	m_vException.Set(_T("callstack"), CallStack());
	if (vOther.Type() != stUndefined) {
		m_vException.Set(_T("other"), vOther);
	}
	else {
		m_vException.Set(_T("other"), AllocNil());
	}

	m_bReturning = RETURN_EXCEPTION;	// Set the returning flag so the interpreter walks
										// up the call stack looking for a catch statement
}

/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////

bool CDialectInterp::MakeExe(TString outputFile, TString interpFilename,
						   TString directory, TString initialScript)
{
	// Remove the possible extension...
	initialScript = ModuleNameFromFilename(initialScript);

	//	outputFile must not be the same as the interpFilename (to avoid accidents)
	if (outputFile.CompareNoCase(interpFilename) == 0) {
		// This isn't perfect, we should get full path on both first...
		RuntimeError(_T("Output file can't be same as interpreter stub in makeexe()"));
		return false;
	}

	// First, compile all .d files, second load all .dc files and store

	CByteCode cs;
	VALUE blobFrame = AllocFrame(97);	// Store the compiled scripts here

	if (directory[directory.GetLength() - 1] != DIR_SEPARATOR) {
		directory += DIR_SEPARATOR;
	}
	TString dirStar = directory + _T("*");

	// Compile all ascii files...
	VALUE arg = dirStar + lpszScriptExt;
	VALUE dirListing = CFileImpl::dir(NULL, 1, &arg, this);
	for (int idx = 1; idx <= dirListing.Length(); ++idx) {
		if (!cs.CompileFile(directory + dirListing[idx].String()) ||
			!cs.SaveToFile(directory + dirListing[idx].String() + _T("c")))
		{
			RuntimeError(cs.GetLastError());
			return false;
		}
	}

	// If the initial module is not to be found, we have an error!
	if (!TFile::Exists(directory + initialScript + lpszPrecompExt)) {
		RuntimeError(_T("Unable to locate initial module in source directory"));
		return false;
	}

	// Now load the compiled versions...
	arg = dirStar + lpszPrecompExt;
	dirListing = CFileImpl::dir(NULL, 1, &arg, this);
	if (dirListing.Length() == 0) {
		RuntimeError(_T("MakeExe failed.  No Dialect script files found in directory: %s"), (LPCTSTR)directory);
		return false;
	}
	for (idx = 1; idx <= dirListing.Length(); ++idx) {
		TFile file;
		file.Open(directory + dirListing[idx].String(), TFile::read);
		int bufSize = file.GetLength();
		U8* buf = new U8[bufSize];
		file.Read(buf, bufSize);
		file.Close();
		blobFrame.Set(ModuleNameFromFilename(dirListing[idx].String()), AllocChunk(bufSize, buf));
		delete[] buf;
	}

	return MakeExe_(outputFile, interpFilename, initialScript, blobFrame);
}

bool CDialectInterp::MakeExe_(TString outputFile, TString interpFilename,
							TString initialScript, VALUE blobFrame)
{
	// Get size of interp, copy it to outputFile, append blobFrame, append tail bytes
	if (!TFile::Exists(interpFilename)) {
		RuntimeError(_T("MakeExe failed: couldn't find stub interpreter: %s"), (LPCTSTR)interpFilename);
		return false;
	}

	TFile::Copy(interpFilename, outputFile);
	SetFileAttributes(outputFile, FILE_ATTRIBUTE_NORMAL); // Make sure it's not read-only!!!

	//TFile file;
	//if (!file.Open(outputFile, TFile::write)) return false;
	CFile file;
	if (!file.Open(outputFile, CFile::modeWrite | CFile::shareDenyWrite | CFile::typeBinary)) {
		return false;
	}

	int originalLen = file.GetLength();
	file.Seek(0, CFile::end);//TFile::end);
	blobFrame.SerializeOut(&file);				// Append frame
	SerializeOutTString(&file, initialScript);	// First script to execute
	SerializeOutInt(&file, originalLen);		// Byte offset of frame
	file.Write("TheEnd", 6);					// Some magic

	file.Close();
	return true;
}

bool CDialectInterp::LoadAppendedFrame()
{
	m_AppendedFrame = AllocNil();
	m_InitialScript.Empty();

	TCHAR exe[512];
	if (!GET_EXE_FILENAME(exe, 512)) {
		ERROR_MSG(_T("Internal Error"), _T("Failed to get exe filename"));
		return false;
	}

	TFile file;
	if (!file.Open(exe, TFile::read)) {
		ERROR_MSG(_T("Internal Error"), _T("Failed to open exe for reading"));
		return false;
	}

	char magic[7] = {0, 0, 0, 0, 0, 0, 0};
	file.Seek(-6, TFile::end);
	int bytes = file.Read(magic, 6);
	if (strcmp("TheEnd", magic) != 0) return false;  // Nothing here

	ASSERT(sizeof(int) == 4);
	U8 offbuf[4];
	file.Seek(-10, TFile::end);
	file.Read(offbuf, 4);
	int offset;
	SerializeInInt(offbuf, offset);

	file.Seek(offset, TFile::begin);	// Goto first byte of serialized code frame
	int bufSize = file.GetLength() - offset + 1;
	U8* buf = new U8[bufSize];
	file.Read(buf, bufSize);

	if (buf[0] != ST_FRAME) {
		ERROR_MSG(_T("Error"), _T("Appended code appears corrupted"));
		delete[] buf;
		file.Close();
		return false;
	}

	U8* head = buf;
	head = m_AppendedFrame.SerializeIn(head);	// Read the frame
	SerializeInTString(head, m_InitialScript);	// Execute me first!

	delete[] buf;
	file.Close();
	return true;
}

bool CDialectInterp::GetAppendedScript(CByteCode* cs, TString name)
{
	if (m_AppendedFrame.Type() != stFrame) return false;

	VALUE script = m_AppendedFrame.Get(name);
	if (script.Type() != stChunk) return false;	// Script not here

	CChunkImpl* chunk = script.Chunk();
	ASSERT(chunk != NULL);
	if (!cs->LoadFromMemory(chunk->GetData())) {
		RuntimeError(cs->GetLastError());
		return false;
	}
	return true;
}

bool CDialectInterp::ExecuteAppended()
{
	CByteCode cs;
	if (!GetAppendedScript(&cs, m_InitialScript)) {
		ERROR_MSG(_T("Error"), _T("Can't load appended initial script"));
		return false;
	}

	// Remove previous exception state...
	m_bReturning = RETURN_NOT;
	m_vException = AllocNil();

	// Temporarily add context to the known modules table
	VALUE context = AllocFrame(97);
	context.Set(_T("__module__"), m_InitialScript);
	m_SymbolTables.Set(m_InitialScript, context);

	int StackLevel = m_DataStack.Level();
	m_DataStack.Push(context.Frame(), NULL, &cs);
	Interpret();
	m_DataStack.PopToLevel(StackLevel);
	return ((m_bReturning == RETURN_NOT) || (m_bReturning == RETURN_FUNCTION));
}

/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
//  The following member functions are the publics
//  None of the publics should be called from within the
//	interpreter since the trap errors which shouldn't
//	be trapped internally.
//	All return false if an exception was raised during
//	interpretation of the data (except for InterpretFile() special case)
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////

// The DO_CATCH_EXC macro is to help trap real exceptions that may have made
// made it passed the exception trap in Interpret()
//#ifdef UNDER_CE
#define DO_CATCH_EXC					\
	_CATCH_EXCEPTION {					\
		DWORD code = GET_EXC_CODE;		\
		LPCTSTR msg = GET_EXC_MESSAGE;	\
		switch (code) {					\
			case EXCEPTION_SYNTAX:		\
				SyntaxError(msg);		\
				break;					\
			case EXCEPTION_RUNTIME:		\
				RuntimeError(msg);		\
				break;					\
			case EXCEPTION_INTERPRETER:	\
			default:					\
				InternalError(msg);		\
		}								\
	}
/*#else
#define DO_CATCH_EXC					\

#endif
	//catch(...) {	\
	//	Error(_T("Error"), _T("AxBasic unhandled exception.  Interpreter may be unstable."));	\
	//}

*/

bool CDialectInterp::InterpretText(LPCTSTR text, VALUE context, LPCTSTR moduleName)
{
	if (context.Type() != stFrame) {
		context = AllocFrame(97);
	}

	if ((moduleName == NULL) || (*moduleName == 0)) {
		if (context.Get(_T("__module__")).Type() != stString) {
			context.Set(_T("__module__"), _T("__main__"));	// Just some bogus value
		}
	}
	else {
		context.Set(_T("__module__"), moduleName);
	}

	// We might actually want to deepclone the context in case we do a restart
	// Problem is that there might be something in there that doesn't want to
	// be cloned.
	//VALUE contextClone = context.DeepClone();

	CByteCode cs;
	_TRY {
		// Remove previous exception state...
		ClearException();

		// Temporarily add context to the known modules table
		m_SymbolTables.Set(moduleName, context);

		if (!cs.CompileText(text, moduleName)) {
			RuntimeError(cs.GetLastError());
		}
		else {
			int StackLevel = m_DataStack.Level();
			m_DataStack.Push(context.Frame(), NULL, &cs);
			Interpret();
			if (m_bReturning != RETURN_DEAD) {
				m_DataStack.PopToLevel(StackLevel);
			}
		}
	}
	DO_CATCH_EXC;

	// Remove context from known symbol tables
	if (m_bReturning != RETURN_DEAD) {
		m_SymbolTables.Frame()->Remove(moduleName);
	}

	if (m_bReturning == RETURN_EXCEPTION) {
		//VALUE vMsg = m_vException.Get(_T("msg"));
		//if (vMsg.Type() != stUndefined)	Error(_T("Exception"), vMsg.String());
	}

	return (m_bReturning != RETURN_EXCEPTION);
}

bool CDialectInterp::Evaluate(LPCTSTR str, VALUE& result, VALUE context)
{
	ClearException();

	if (context.Type() != stFrame) {
		context = AllocFrame(97);
	}

	int StackLevel = m_DataStack.Level();

	_TRY {
		EvaluateText(str, result, context.Frame());
	}
	DO_CATCH_EXC;

	m_DataStack.PopToLevel(StackLevel);

	return (m_bReturning != RETURN_EXCEPTION);
}

bool CDialectInterp::EvaluateFile(LPCTSTR filename, VALUE& result, VALUE& context)
{
	TFile file;
	if (!file.Open(filename, TFile::read)) {
		TString error;
		error.Format(_T("EvaluateFile() failed to open file: %s"), filename);
		RuntimeError(error);
		return false;
	}

	// All files are stored in ASCII, not unicode
	int bufLength = file.GetLength();
	char* str = new char[bufLength + 1];
	file.Read(str, bufLength);
	str[bufLength] = '\0';
	file.Close();

#ifdef UNICODE
	TCHAR* buf = new TCHAR[bufLength + 1];
	for (int idx = 0; idx <= bufLength; ++idx) {
		buf[idx] = str[idx];
	}
	delete[] str;
	str = (char*)buf;	// Let's coax it a little...
#endif

	bool b = Evaluate((LPCTSTR)str, result, context);
	delete[] str;
	return b;
}

bool CDialectInterp::Execute(VALUE func, VALUE context, int argCount, VALUE args[],
						   VALUE& result)
{
	result = AllocNil();	// Clear result in case of exception

	if (context.Type() != stFrame) {
		context = AllocFrame(97);
	}

	// Remove previous exception state, but first save it
	// because if this function is called recursively from
	// outside the interpreter, we could prematurely end
	// a function call, etc.
	// Downside, kills correct exception reporting
	//VALUE vOldException = m_vException;
	//int oldReturning = m_bReturning;
	ClearException();

	bool bResult = false;
	VALUE symtab;
	int OldPos = -1, OldLine, StackLevel = m_DataStack.Level();
	TString funcModule;
	CFunctionImpl* funcImpl = NULL;
	_TRY {
		if (func.Type() != stFunction) {
			InternalError(_T("VALUE passed to Execute() isn't a function"));
			#ifdef USE_SEH
				__leave;
			#else
				return false;
			#endif
		}
		else if (argCount != func.Function()->m_ParamCount) {
			SyntaxError(_T("Invalid number of arguments in function call"));
			#ifdef USE_SEH
				__leave;
			#else
				return false;
			#endif
		}

		funcImpl = func.Function();

		// Create new symboltable, place it on the symbaletable stack
		symtab = AllocFrame(31);
		symtab.Set(_T("__result__"), AllocNil());

		// Match up args with declared parms and place in new local symbol table
		for (int idx = 0; idx < argCount; idx++) {
			symtab.Set(funcImpl->m_ParamList[idx], args[idx]);
		}

		// There can only be a "this" if there is an associated class instance implementation
		if (funcImpl->m_pClassInstImpl) {
			VALUE forcedClassInst;
			forcedClassInst.ForceCI(stClassInstance, funcImpl->m_pClassInstImpl);
			symtab.Set(_T("this"), forcedClassInst);
		}

		// There can only be an "inherited" if there is an associated class implementation
		if ((funcImpl->m_pClassImpl != NULL) &&
			(funcImpl->m_pClassImpl->m_BaseClass.Type() == stClass)) {
			symtab.Set(_T("inherited"), funcImpl->m_pClassImpl->m_BaseClass);
		}

		OldPos = funcImpl->m_CodeStream->Position() - 1;
		OldLine = funcImpl->m_CodeStream->LineNumber();
		funcImpl->m_CodeStream->Seek(0, 1);	// Rewind the stream first!

		if (funcImpl->m_pClassInstImpl) {
			m_DataStack.Push(context.Frame(), symtab.Frame(), funcImpl->m_CodeStream, funcImpl->m_pClassInstImpl);
		}
		else {
			m_DataStack.Push(context.Frame(), symtab.Frame(), funcImpl->m_CodeStream, funcImpl->m_pClassImpl);
		}
		Interpret();
		m_DataStack.PopToLevel(StackLevel);

		// Get the functions return value
		result = symtab.Get(_T("__result__"));

		// Restore state and get ready to leave
		//m_vException = vOldException;
		//m_bReturning = oldReturning;
		bResult = true;
	}
	DO_CATCH_EXC;

	if (m_bReturning == RETURN_EXCEPTION) {
		bResult = false;
	//	VALUE vMsg = m_vException.Get(_T("msg"));
	//	if (vMsg.Type() != stUndefined)	Error(_T("Exception"), vMsg.String());
	}

	if (funcImpl != NULL) {
		if (OldPos != -1) funcImpl->m_CodeStream->Seek(OldPos, OldLine);	// Put it back (for correct recursion)
	}
	return bResult;
}

bool CDialectInterp::InterpretFile(LPCTSTR filename)
{
	// This is only called for the very first script!!!
	// This isn't used by the 'import' or 'run' statements.
	ClearException();

	VALUE modSymTab;
	CByteCode cs;
	TString moduleName = ModuleNameFromFilename(filename);
	int StackLevel = m_DataStack.Level();

	if (!LoadScript(&cs, moduleName)) {
		return false;
	}

#ifdef DIALECT_LITE
	m_ImportCount = 0;	// Don't count the above import in the total
#endif

	// Create the modules private symbol table and store it
	modSymTab = AllocFrame(51);
	m_SymbolTables.Set(moduleName, modSymTab);

	m_DataStack.Push(modSymTab.Frame(), NULL, &cs);
	Interpret();
	m_DataStack.PopToLevel(StackLevel);

	return ((m_bReturning == RETURN_NOT) || (m_bReturning == RETURN_FUNCTION));
}

bool CDialectInterp::DebugInterpretText(LPCTSTR text, TString& exceptionText)
{
	// If an exception occurs, trap it and remove it before exiting...

	exceptionText.Empty();

	int StackLevel = m_DataStack.Level();

	CByteCode cs;

	// Remove any previous exception state...
	ClearException();

	if (!cs.CompileText(text, CurrentModuleName())) {
		RuntimeError(cs.GetLastError());
	}
	else {
		// Push all the current contexts back on the stack, just change code stream
		m_DataStack.Push(m_DataStack.ModuleSymTab(), m_DataStack.FunctionSymTab(),
			&cs, m_DataStack.ClassImpl());
		Interpret();
		if (m_bReturning != RETURN_DEAD) {
			m_DataStack.PopToLevel(StackLevel);
		}
	}

	bool bResult = true;
	if (m_bReturning == RETURN_EXCEPTION) {
		VALUE vMsg = m_vException.Get(_T("msg"));
		if (vMsg.Type() != stUndefined)	{
			exceptionText = vMsg.String();
		}
		ClearException();	// Don't leave an exception state in the interpreter
		bResult = false;
	}
	return bResult;
}

bool CDialectInterp::DebugEvaluate(LPCTSTR text, VALUE& result, TString& exceptionText)
{
	bool bResult = true;
	result = AllocNil();	// Should it be this, or stUndefined?

	CByteCode cs;
	if (!cs.CompileText(text, CurrentModuleName())) {
		RuntimeError(cs.GetLastError());
	}
	else {
		int StackLevel = m_DataStack.Level();
		// Push all the current contexts back on the stack, just change code stream
		m_DataStack.Push(m_DataStack.ModuleSymTab(), m_DataStack.FunctionSymTab(),
			&cs, m_DataStack.ClassImpl());
		Expression(result);
		m_DataStack.PopToLevel(StackLevel);
	}

	if (m_bReturning == RETURN_EXCEPTION) {
		VALUE vMsg = m_vException.Get(_T("msg"));
		if (vMsg.Type() != stUndefined)	{
			exceptionText = vMsg.String();
		}
		ClearException();	// Don't leave an exception state in the interpreter
		bResult = false;
	}
	return bResult;
}

/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
//  The following member functions are the publics
//	intended to set intial default I/O paths, so if the
//	script doesn't specifically set an output.
//	For example, a Unix versions might just assign the
//	stdio funcs.
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////

CDefaultInImpl::CDefaultInImpl():CClassImpl(VALUE(), _T("defaultin"))
{
	m_MemberSymTab = AllocFrame(19);
	// Anything added here is static (becomes shared by all instances of the class)
	m_MemberSymTab.Set(_T("__type__"), (LPCTSTR)_T("defaultin"));
}

CDefaultInImpl::~CDefaultInImpl(){}

CDefaultOutImpl::CDefaultOutImpl():CClassImpl(VALUE(), _T("defaultout"))
{
	m_MemberSymTab = AllocFrame(19);
	// Anything added here is static (becomes shared by all instances of the class)
	m_MemberSymTab.Set(_T("__type__"), (LPCTSTR)_T("defaultout"));
}

CDefaultOutImpl::~CDefaultOutImpl(){}

////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////

void CDialectInterp::SetDefaultRead(TString (*readFunc)(int count))
{
	m_pDefaultReadFunc = readFunc;
}

void CDialectInterp::SetDefaultReadLine(TString (*readLineFunc)())
{
	m_pDefaultReadLineFunc = readLineFunc;
}

void CDialectInterp::SetDefaultInputOther(TString name, VALUE vOther)
{
	m_vDefaultInInst.Set(name, vOther);
}

void CDialectInterp::SetDefaultWrite(void (*writeFunc)(TString text))
{
	m_pDefaultWriteFunc = writeFunc;
}

void CDialectInterp::SetDefaultWriteLine(void (*writeLineFunc)(TString text))
{
	m_pDefaultWriteLineFunc = writeLineFunc;
}

void CDialectInterp::SetDefaultOutputOther(TString name, VALUE vOther)
{
	m_vDefaultOutInst.Set(name, vOther);
}

////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////

DataStack_t::DataStack_t():m_Top(-1){};
CFrameImpl* DataStack_t::ModuleSymTab() {return m_Top > -1 ? m_ModuleSymTab[m_Top] : NULL;};
CFrameImpl* DataStack_t::FunctionSymTab() {return m_Top > -1 ? m_FunctionSymTab[m_Top] : NULL;};
CClassImpl* DataStack_t::ClassImpl() {return m_Top > -1 ? m_ClassImpl[m_Top] : NULL;};
CByteCode* DataStack_t::CodeStream() {return m_Top > -1 ? m_CodeStreamNode[m_Top].m_CodeStream : NULL;};
VALUE* DataStack_t::WithValuePtr() {return m_Top > -1 ? m_pvWith[m_Top] : NULL;};
void DataStack_t::SetWithValuePtr(VALUE* pvWith) {m_pvWith[m_Top] = pvWith;};
void DataStack_t::Push(CFrameImpl* modSymTab, CFrameImpl* funcSymTab, CByteCode* codeStream, CClassImpl* classImpl)
{
	// Possible optimization: I don't think m_CodeStreamNode[m_Top].m_SavedPosition is
	// actually needed anymore.  See if we can't remove it.

	++m_Top;
	if (m_Top >= MAX_CALL_DEPTH) {
		--m_Top;
		InternalError(_T("Exceeded maximum symbol table stack depth"));
	}
	else {
		m_ModuleSymTab[m_Top] = modSymTab;
		m_FunctionSymTab[m_Top] = funcSymTab;
		m_ClassImpl[m_Top] = classImpl;
		m_pvWith[m_Top] = NULL;

		/*
		 *	We save the current position in case of recursive calls
		 */
		if ((m_Top >= 1) && (m_CodeStreamNode[m_Top - 1].m_CodeStream != NULL)) {
			m_CodeStreamNode[m_Top - 1].m_SavedPosition = m_CodeStreamNode[m_Top - 1].m_CodeStream->Position();
		}
		m_CodeStreamNode[m_Top].m_CodeStream = codeStream;
		// codeStream could be NULL if executing core function
		if (codeStream != NULL) {
			m_CodeStreamNode[m_Top].m_SavedPosition = codeStream->Position();
		}
	}
};

int DataStack_t::Level() const {return m_Top;};
void DataStack_t::PopToLevel(int level)
{
	ASSERT((level >= -1) && (level <= m_Top));
	m_Top = level;
}

void DataStack_t::Pop()
{
	if (--m_Top < -1) {
		InternalError(_T("Popped data stack into oblivion"));
		m_Top = -1;
	}
}

VALUE DataStack_t::CallStack()
{
	ASSERT(m_Top >= -1);
	VALUE result = AllocArray(m_Top + 1);
	for (int idx = m_Top + 1; idx > 0; --idx) {
		VALUE item = AllocArray(2);
		result.Set(idx, item);

		CByteCode* cs = m_CodeStreamNode[idx - 1].m_CodeStream;
		if (cs == NULL) {
			TString module;
			if (m_ModuleSymTab[m_Top] != NULL) {
				module = m_ModuleSymTab[m_Top]->__get__(_T("__module__")).String();
			}
			if (module.IsEmpty()) module = _T("__core__");
			item.Set(1, module);				// Module name
			item.Set(2, 0);						// Line number
		}
		else {
			_TRY {
				item.Set(1, cs->ModuleName());	// Module name
				item.Set(2, cs->LineNumber());	// Line number
			}
			_CATCH_ALL {
				item.Set(1, _T("Callstack corruption"));	// Module name
				item.Set(2, 0);	// Line number
			}
		}
	}
	return result;
}

int CDialectInterp::GetReturnFlag() const
{
	return m_bReturning;
}

VALUE CDialectInterp::GetException()
{
	// int m_bReturning;
	// VALUE m_vException;
		// If m_bReturning == true and m_vException.Type() == stFrame
		// Then we are passing a thrown exception up the call stack

	return m_vException;
}

void CDialectInterp::SetExceptionInstance(VALUE vClassInstance)
{
	if ((vClassInstance.Type() == stClassInstance) ||
		(vClassInstance.Type() == stNil) ||
		(vClassInstance.Type() == stUndefined))
	{
		m_vException = vClassInstance;
	}
	else {
		ASSERT(false);
		m_vException = AllocNil();
	}
}

//////////////////////////////////////////////////////////////////////
